diff --git a/templates/rde/README.md b/templates/rde/README.md new file mode 100644 index 0000000..3cb6119 --- /dev/null +++ b/templates/rde/README.md @@ -0,0 +1,65 @@ +# Research Development Environment (RDE) Template + +This is a Nix flake template for setting up research development environments with support for R, Python, and Julia. + +## Structure + +The template is organized into several directories for better maintainability: + +``` +templates/rde/ +├── flake.nix # Main flake configuration (290 lines) +├── 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.) +└── scripts/ # Shell scripts + ├── initPython.sh # Initialize Python project + ├── initProject.sh # Initialize project structure + ├── updateDeps.sh # Update all dependencies + └── activateDevenv.sh # Activate devenv shell +``` + +## 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 now ~290 lines instead of 688 +4. **Reusability**: Individual modules can be easily reused or replaced +5. **Testability**: Smaller files are easier to test and debug + +## Configuration + +Edit the `config` section in `flake.nix` to customize: + +- `defaultPackageName`: Name of your project/package +- `enabledLanguages`: Enable/disable R, Python, Julia support +- `enabledPackages`: Enable additional features like devenv +- `theme`: Configure Neovim color scheme + +## Extending + +To add new functionality: + +- **New packages**: Add overlays in `overlays/` +- **New commands**: Add host configs in `hosts/` +- **New scripts**: Add shell scripts in `scripts/` +- **New languages**: Create new host and overlay files + +## 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.nix b/templates/rde/flake.nix index 78a2828..eb92de0 100644 --- a/templates/rde/flake.nix +++ b/templates/rde/flake.nix @@ -50,241 +50,24 @@ }; }; }; - # R packages - rOverlay = final: prev: let - reqPkgs = with final.rpkgs.rPackages; - [ - broom - data_table - janitor - languageserver - reprex - styler - tidyverse - ] - ++ (with final.extraRPackages; [ - httpgd - ]) - ++ (prev.lib.optional (builtins.pathExists ./r-packages.nix) (import ./r-packages.nix final.rpkgs)); - 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 ⬆️ ## ################################### - rixOverlay = final: prev: {rpkgs = inputs.rixpkgs.legacyPackages.${prev.stdenv.hostPlatform.system};}; - - extraPkgOverlay = final: prev: let - extraTheme = { - plugin = prev.vimPlugins."${config.theme.extraColorschemePackage.plugin}"; - name = config.theme.extraColorschemePackage.name; - config = { - lua = config.theme.extraColorschemePackage.extraLua; - }; - }; - in { - inherit extraTheme; - }; - - 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 - ''; - - initProjectScript = '' - #!/usr/bin/env bash - set -euo pipefail - - 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 - - # Devenv - .devenv* - devenv.local.nix - - # direnv - .direnv - - # pre-commit - .pre-commit-config.yaml - EOF - fi - - echo "✅ Project setup completed successfully!" - ''; - - updateDepsScript = '' - #!/usr/bin/env bash - set -euo pipefail - - echo "🔄 Updating project dependencies..." - - 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 '"' ) &&\ - - 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 - echo "✅ R date is $RVER" - - nix flake update - echo "✅ Flake inputs updated" - - 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 - - if [[ -f "devenv.nix" ]]; then - devenv update - echo "✅ Devenv dependencies updated" - fi - - echo "🎉 All dependencies updated!" - ''; - - activateDevenv = '' - #!/usr/bin/env bash - set -euo pipefail - if [[ -f "devenv.nix" ]]; then - echo "🚀 Activating devenv environment..." - exec ${config.defaultPackageName}-devenv shell - else - echo "❌ No devenv.nix file found in the current directory." - echo "To create one, run '${config.defaultPackageName}-initDevenv'" - exit 1 - fi - ''; - in { - initPython = prev.writeShellScriptBin "initPython" initPython; - initProject = prev.writeShellScriptBin "initProject" initProjectScript; - updateDeps = prev.writeShellScriptBin "updateDeps" updateDepsScript; - activateDevenv = prev.writeShellScriptBin "activateDevenv" activateDevenv; - }; + # Import overlays from separate files + 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; supportedSystems = [ "x86_64-linux" "aarch64-linux" "aarch64-darwin" ]; forSystems = nixpkgs.lib.genAttrs supportedSystems; + projectConfig = forSystems ( system: let inherit (nixCats) utils; @@ -303,6 +86,7 @@ pythonOverlay projectScriptsOverlay ]; + categoryDefinitions = utils.mergeCatDefs prev.categoryDefinitions ( { pkgs, @@ -314,28 +98,14 @@ ... } @ packageDef: { lspsAndRuntimeDeps = { - project = with pkgs; [ - ]; - julia = with pkgs; [ - julia-bin - ]; - python = with pkgs; [ - python - nodejs - basedpyright - 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]; }; startupPlugins = { - project = with pkgs.vimPlugins; [ - pkgs.extraTheme - ]; + project = with pkgs.vimPlugins; [pkgs.extraTheme]; gitPlugins = with pkgs.neovimPlugins; [ { plugin = r; @@ -345,13 +115,13 @@ }; optionalPlugins = { - project = with pkgs.vimPlugins; [ - ]; + project = with pkgs.vimPlugins; []; gitPlugins = with pkgs.neovimPlugins; [ cmp-r cmp-pandoc-references ]; }; + optionalLuaPreInit = { project = [ '' @@ -370,37 +140,27 @@ '' ]; }; + optionalLuaAdditions = { - project = [ - "vim.notify('Project loaded: ${name}')" - ]; + project = ["vim.notify('Project loaded: ${name}')"]; }; + sharedLibraries = { - project = { - }; + project = {}; }; 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"]; }; } ); @@ -417,166 +177,8 @@ 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}" - ]; - }; - }; - 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 - ''; - in { - enable = config.enabledLanguages.python; - path = { - value = "${pkgs.uv}/bin/uv"; - args = [ - "--run" - "${marimoInit}" - "--add-flags" - "run marimo edit \"$@\"" - ]; - }; - }; - py = { - enable = config.enabledLanguages.python; - path = { - value = "${pkgs.python.interpreter}"; - }; - }; - 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..." - 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(); Pkg.add(\"Pluto\")'"]; - }; - }; - pluto = let - runPluto = '' - import Pkg; import TOML; Pkg.instantiate(); - if !isfile("Project.toml") || !haskey(TOML.parsefile(Base.active_project())["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}'"]; - }; - }; - 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"; - }; - }; - initDevenv = { - enable = config.enabledPackages.devenv; - path = { - value = "${pkgs.devenv}/bin/devenv"; - args = ["--add-flags" "init"]; - }; - }; - activateDevenv = { - enable = config.enabledPackages.devenv; - path = { - value = "${pkgs.activateDevenv}/bin/activateDevenv"; - }; - }; - devenv = { - enable = config.enabledPackages.devenv; - path = { - value = "${pkgs.devenv}/bin/devenv"; - }; - }; - updateDeps = { - enable = true; - path = { - value = "${pkgs.updateDeps}/bin/updateDeps"; - }; - }; - node.enable = true; - perl.enable = true; - ruby.enable = true; - }; + hosts = import ./hosts config pkgs; }; categories = { julia = config.enabledLanguages.julia; diff --git a/templates/rde/flake.nix.backup b/templates/rde/flake.nix.backup new file mode 100644 index 0000000..78a2828 --- /dev/null +++ b/templates/rde/flake.nix.backup @@ -0,0 +1,688 @@ +{ + description = "New Project"; + + outputs = { + self, + nixpkgs, + nixCats, + ... + } @ inputs: let + ####################### + ### PROJECT CONFIG #### + ####################### + ## Set options below: + config = rec { + ## Set project name + defaultPackageName = "p"; + ## Enable languages + enabledLanguages = { + julia = false; + python = false; + r = true; + }; + ## Enable packages + enabledPackages = { + ## Plugins loaded via flake input + ### 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 + rOverlay = final: prev: let + reqPkgs = with final.rpkgs.rPackages; + [ + broom + data_table + janitor + languageserver + reprex + styler + tidyverse + ] + ++ (with final.extraRPackages; [ + httpgd + ]) + ++ (prev.lib.optional (builtins.pathExists ./r-packages.nix) (import ./r-packages.nix final.rpkgs)); + 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 ⬆️ ## + ################################### + + rixOverlay = final: prev: {rpkgs = inputs.rixpkgs.legacyPackages.${prev.stdenv.hostPlatform.system};}; + + extraPkgOverlay = final: prev: let + extraTheme = { + plugin = prev.vimPlugins."${config.theme.extraColorschemePackage.plugin}"; + name = config.theme.extraColorschemePackage.name; + config = { + lua = config.theme.extraColorschemePackage.extraLua; + }; + }; + in { + inherit extraTheme; + }; + + 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 + ''; + + initProjectScript = '' + #!/usr/bin/env bash + set -euo pipefail + + 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 + + # Devenv + .devenv* + devenv.local.nix + + # direnv + .direnv + + # pre-commit + .pre-commit-config.yaml + EOF + fi + + echo "✅ Project setup completed successfully!" + ''; + + updateDepsScript = '' + #!/usr/bin/env bash + set -euo pipefail + + echo "🔄 Updating project dependencies..." + + 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 '"' ) &&\ + + 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 + echo "✅ R date is $RVER" + + nix flake update + echo "✅ Flake inputs updated" + + 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 + + if [[ -f "devenv.nix" ]]; then + devenv update + echo "✅ Devenv dependencies updated" + fi + + echo "🎉 All dependencies updated!" + ''; + + activateDevenv = '' + #!/usr/bin/env bash + set -euo pipefail + if [[ -f "devenv.nix" ]]; then + echo "🚀 Activating devenv environment..." + exec ${config.defaultPackageName}-devenv shell + else + echo "❌ No devenv.nix file found in the current directory." + echo "To create one, run '${config.defaultPackageName}-initDevenv'" + exit 1 + fi + ''; + in { + initPython = prev.writeShellScriptBin "initPython" initPython; + initProject = prev.writeShellScriptBin "initProject" initProjectScript; + updateDeps = prev.writeShellScriptBin "updateDeps" updateDepsScript; + activateDevenv = prev.writeShellScriptBin "activateDevenv" activateDevenv; + }; + supportedSystems = [ + "x86_64-linux" + "aarch64-linux" + "aarch64-darwin" + ]; + forSystems = nixpkgs.lib.genAttrs supportedSystems; + projectConfig = forSystems ( + system: let + inherit (nixCats) utils; + inherit (config) defaultPackageName; + prevPackage = nixCats.packages.${system}.default; + finalPackage = prevPackage.override (prev: { + name = config.defaultPackageName; + dependencyOverlays = + prev.dependencyOverlays + ++ [ + (utils.standardPluginOverlay inputs) + extraPkgOverlay + rixOverlay + inputs.fran.overlays.default + rOverlay + pythonOverlay + projectScriptsOverlay + ]; + categoryDefinitions = utils.mergeCatDefs prev.categoryDefinitions ( + { + pkgs, + settings, + categories, + name, + extra, + mkPlugin, + ... + } @ packageDef: { + lspsAndRuntimeDeps = { + project = with pkgs; [ + ]; + julia = with pkgs; [ + julia-bin + ]; + python = with pkgs; [ + python + nodejs + basedpyright + uv + ]; + r = with pkgs; [ + rWrapper + quarto + air-formatter + ]; + }; + + startupPlugins = { + project = with pkgs.vimPlugins; [ + pkgs.extraTheme + ]; + gitPlugins = with pkgs.neovimPlugins; [ + { + plugin = r; + config.lua = "vim.notify('Using project local R plugin')"; + } + ]; + }; + + optionalPlugins = { + project = with pkgs.vimPlugins; [ + ]; + gitPlugins = with pkgs.neovimPlugins; [ + cmp-r + cmp-pandoc-references + ]; + }; + 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() + '' + ]; + }; + optionalLuaAdditions = { + project = [ + "vim.notify('Project loaded: ${name}')" + ]; + }; + sharedLibraries = { + project = { + }; + }; + + environmentVariables = { + 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"; + }; + }; + + extraWrapperArgs = { + python = [ + "--unset PYTHONPATH" + ]; + }; + } + ); + + packageDefinitions = + prev.packageDefinitions + // { + "${config.defaultPackageName}" = utils.mergeCatDefs prev.packageDefinitions.n ( + { + pkgs, + name, + ... + }: { + 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}" + ]; + }; + }; + 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 + ''; + in { + enable = config.enabledLanguages.python; + path = { + value = "${pkgs.uv}/bin/uv"; + args = [ + "--run" + "${marimoInit}" + "--add-flags" + "run marimo edit \"$@\"" + ]; + }; + }; + py = { + enable = config.enabledLanguages.python; + path = { + value = "${pkgs.python.interpreter}"; + }; + }; + 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..." + 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(); Pkg.add(\"Pluto\")'"]; + }; + }; + pluto = let + runPluto = '' + import Pkg; import TOML; Pkg.instantiate(); + if !isfile("Project.toml") || !haskey(TOML.parsefile(Base.active_project())["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}'"]; + }; + }; + 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"; + }; + }; + initDevenv = { + enable = config.enabledPackages.devenv; + path = { + value = "${pkgs.devenv}/bin/devenv"; + args = ["--add-flags" "init"]; + }; + }; + activateDevenv = { + enable = config.enabledPackages.devenv; + path = { + value = "${pkgs.activateDevenv}/bin/activateDevenv"; + }; + }; + devenv = { + enable = config.enabledPackages.devenv; + path = { + value = "${pkgs.devenv}/bin/devenv"; + }; + }; + updateDeps = { + enable = true; + path = { + value = "${pkgs.updateDeps}/bin/updateDeps"; + }; + }; + node.enable = true; + perl.enable = true; + ruby.enable = true; + }; + }; + 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; + }; + } + ); + }; + }); + in + utils.mkAllWithDefault finalPackage + ); + in { + packages = projectConfig; + devShells = forSystems (system: let + pkgs = import nixpkgs {inherit system;}; + 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}-pluto: Launch Pluto.jl notebook") + (pkgs.lib.optionalString config.enabledLanguages.julia " - ${config.defaultPackageName}-initJl: Init existing Julia project") + (pkgs.lib.optionalString config.enabledLanguages.python " - ${config.defaultPackageName}-marimo: Launch Marimo notebook") + (pkgs.lib.optionalString config.enabledLanguages.python " - ${config.defaultPackageName}-py: Run python") + (pkgs.lib.optionalString config.enabledLanguages.python " - ${config.defaultPackageName}-ipy: Launch IPython REPL") + (pkgs.lib.optionalString config.enabledLanguages.python " - ${config.defaultPackageName}-initPython: Init python project") + (pkgs.lib.optionalString config.enabledPackages.devenv " - ${config.defaultPackageName}-initDevenv: Init devenv project") + (pkgs.lib.optionalString config.enabledPackages.devenv " - ${config.defaultPackageName}-devenv: Run devenv") + " " + "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 "" + ${pkgs.lib.optionalString config.enabledPackages.devenv "${config.defaultPackageName}-activateDevenv"} + echo "" + ''; + }; + }); + }; + inputs = { + rixpkgs.url = "github:rstats-on-nix/nixpkgs/2025-12-15"; + 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"; + nvimcom.follows = "plugins-r"; + }; + }; + ## 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..e376a20 --- /dev/null +++ b/templates/rde/hosts/default.nix @@ -0,0 +1,8 @@ +# Merges all host configurations from separate modules +config: pkgs: let + 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 + pythonHosts // juliaHosts // rHosts // utilsHosts diff --git a/templates/rde/hosts/julia.nix b/templates/rde/hosts/julia.nix new file mode 100644 index 0000000..c5e2883 --- /dev/null +++ b/templates/rde/hosts/julia.nix @@ -0,0 +1,34 @@ +# Julia-related host configurations +config: pkgs: { + 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(); Pkg.add(\"Pluto\")'"]; + }; + }; + + pluto = let + runPluto = '' + import Pkg; import TOML; Pkg.instantiate(); + if !isfile("Project.toml") || !haskey(TOML.parsefile(Base.active_project())["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..3ad0c3d --- /dev/null +++ b/templates/rde/hosts/python.nix @@ -0,0 +1,82 @@ +# Python-related host configurations +config: pkgs: { + 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 + ''; + in { + enable = config.enabledLanguages.python; + path = { + value = "${pkgs.uv}/bin/uv"; + args = [ + "--run" + "${marimoInit}" + "--add-flags" + "run marimo edit \"$@\"" + ]; + }; + }; + + py = { + enable = config.enabledLanguages.python; + path = { + value = "${pkgs.python.interpreter}"; + }; + }; + + 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..." + 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 \"$@\"" + ]; + }; + }; + + 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..971c580 --- /dev/null +++ b/templates/rde/hosts/r.nix @@ -0,0 +1,10 @@ +# R-related host configurations +config: pkgs: { + 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..ab90675 --- /dev/null +++ b/templates/rde/hosts/utils.nix @@ -0,0 +1,53 @@ +# Utility and common host configurations +config: pkgs: { + g = { + enable = true; + path = { + value = "${pkgs.neovide}/bin/neovide"; + args = [ + "--add-flags" + "--neovim-bin ${config.defaultPackageName}" + ]; + }; + }; + + initProject = { + enable = true; + path = { + value = "${pkgs.initProject}/bin/initProject"; + }; + }; + + initDevenv = { + enable = config.enabledPackages.devenv; + path = { + value = "${pkgs.devenv}/bin/devenv"; + args = ["--add-flags" "init"]; + }; + }; + + activateDevenv = { + enable = config.enabledPackages.devenv; + path = { + value = "${pkgs.activateDevenv}/bin/activateDevenv"; + }; + }; + + devenv = { + enable = config.enabledPackages.devenv; + path = { + value = "${pkgs.devenv}/bin/devenv"; + }; + }; + + updateDeps = { + enable = true; + path = { + value = "${pkgs.updateDeps}/bin/updateDeps"; + }; + }; + + node.enable = true; + perl.enable = true; + ruby.enable = true; +} diff --git a/templates/rde/overlays/project-scripts.nix b/templates/rde/overlays/project-scripts.nix new file mode 100644 index 0000000..a8c227b --- /dev/null +++ b/templates/rde/overlays/project-scripts.nix @@ -0,0 +1,14 @@ +# Project scripts overlay +config: final: prev: let + # Helper function to substitute config placeholders in scripts + substituteScript = scriptPath: + prev.lib.replaceStrings + ["@defaultPackageName@"] + [config.defaultPackageName] + (builtins.readFile scriptPath); +in { + initPython = prev.writeShellScriptBin "initPython" (substituteScript ./scripts/initPython.sh); + initProject = prev.writeShellScriptBin "initProject" (substituteScript ./scripts/initProject.sh); + updateDeps = prev.writeShellScriptBin "updateDeps" (substituteScript ./scripts/updateDeps.sh); + 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..8ad4fb6 --- /dev/null +++ b/templates/rde/overlays/python.nix @@ -0,0 +1,7 @@ +# Python packages overlay +final: prev: { + python = prev.python3.withPackages (pyPackages: + with pyPackages; [ + requests + ]); +} diff --git a/templates/rde/overlays/r.nix b/templates/rde/overlays/r.nix new file mode 100644 index 0000000..4b3136d --- /dev/null +++ b/templates/rde/overlays/r.nix @@ -0,0 +1,20 @@ +# R packages overlay +final: prev: let + reqPkgs = with final.rpkgs.rPackages; + [ + broom + data_table + janitor + languageserver + reprex + styler + tidyverse + ] + ++ (with final.extraRPackages; [ + httpgd + ]) + ++ (prev.lib.optional (builtins.pathExists ./r-packages.nix) (import ./r-packages.nix final.rpkgs)); +in { + quarto = final.rpkgs.quarto.override {extraRPackages = reqPkgs;}; + 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..2a83615 --- /dev/null +++ b/templates/rde/overlays/rix.nix @@ -0,0 +1,4 @@ +# Rix overlay for R packages from rstats-on-nix +inputs: final: prev: { + 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..6d242a6 --- /dev/null +++ b/templates/rde/overlays/theme.nix @@ -0,0 +1,12 @@ +# Extra theme packages overlay +config: final: prev: let + extraTheme = { + plugin = prev.vimPlugins."${config.theme.extraColorschemePackage.plugin}"; + name = config.theme.extraColorschemePackage.name; + config = { + lua = config.theme.extraColorschemePackage.extraLua; + }; + }; +in { + inherit extraTheme; +} diff --git a/templates/rde/scripts/activateDevenv.sh b/templates/rde/scripts/activateDevenv.sh new file mode 100644 index 0000000..e3e4eaa --- /dev/null +++ b/templates/rde/scripts/activateDevenv.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash +set -euo pipefail +if [[ -f "devenv.nix" ]]; then + echo "🚀 Activating devenv environment..." + 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..c616268 --- /dev/null +++ b/templates/rde/scripts/initProject.sh @@ -0,0 +1,104 @@ +#!/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 + 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 + +# 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..7059cde --- /dev/null +++ b/templates/rde/scripts/initPython.sh @@ -0,0 +1,21 @@ +#!/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 '@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..d0d0c9c --- /dev/null +++ b/templates/rde/scripts/updateDeps.sh @@ -0,0 +1,29 @@ +#!/usr/bin/env bash +set -euo pipefail + +echo "🔄 Updating project dependencies..." + +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 '"' ) &&\ + +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 +echo "✅ R date is $RVER" + +nix flake update +echo "✅ Flake inputs updated" + +if [[ -f "pyproject.toml" ]]; then + uv sync --upgrade + echo "✅ Python dependencies updated" +fi + +if [[ -f "Project.toml" ]]; then + @defaultPackageName@-jl -e "using Pkg; Pkg.update()" + echo "✅ Julia dependencies updated" +fi + +if [[ -f "devenv.nix" ]]; then + devenv update + echo "✅ Devenv dependencies updated" +fi + +echo "🎉 All dependencies updated!"