mirror of
https://github.com/nix-community/home-manager.git
synced 2026-01-12 01:59:37 +08:00
Compare commits
40 Commits
putter-poc
...
9d32c214db
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9d32c214db | ||
|
|
87785ddbc7 | ||
|
|
113b155fe8 | ||
|
|
398bc87bc8 | ||
|
|
3613abcbd7 | ||
|
|
d7e794fe12 | ||
|
|
8969535f1c | ||
|
|
80cca72314 | ||
|
|
d2e0458d65 | ||
|
|
b3ae822959 | ||
|
|
d761c0ce89 | ||
|
|
f84f474c1b | ||
|
|
ea6dfabe3c | ||
|
|
5432dc5bc4 | ||
|
|
bec08ef6e3 | ||
|
|
4067ca1ffb | ||
|
|
2d36a6de2f | ||
|
|
91cdb0e2d5 | ||
|
|
7eca7f7081 | ||
|
|
20728df08f | ||
|
|
af3c24de76 | ||
|
|
624c7e80fb | ||
|
|
9c790e687e | ||
|
|
527ad07e66 | ||
|
|
57a02fd7d9 | ||
|
|
3fe66908e0 | ||
|
|
61fcc9de76 | ||
|
|
7b73a6e98f | ||
|
|
4dc3c91c50 | ||
|
|
a7d6bba358 | ||
|
|
bdb807dc28 | ||
|
|
0a583021ea | ||
|
|
ab01ea24b2 | ||
|
|
c764a377a0 | ||
|
|
28b3622b80 | ||
|
|
c848303f1e | ||
|
|
bb35f07cc9 | ||
|
|
9bf54edf10 | ||
|
|
89c9508bbe | ||
|
|
22202ff0d8 |
@@ -2078,7 +2078,7 @@
|
||||
source = "nixpkgs";
|
||||
};
|
||||
shikanime = {
|
||||
email = "deva.shikanime@protonmail.com";
|
||||
email = "william.phetsinorath@shikanime.studio";
|
||||
github = "shikanime";
|
||||
githubId = 22115108;
|
||||
name = "William Phetsinorath";
|
||||
|
||||
@@ -57,3 +57,9 @@ changes are only active if the `home.stateVersion` option is set to
|
||||
|
||||
now default to `true` which is consistent with the default values
|
||||
for those options used by `i3` and `sway`.
|
||||
|
||||
- The [](#opt-programs.swaylock.enable) option now defaults to `false`
|
||||
and must be explicitly enabled. Previously, it would be implicitly
|
||||
enabled when `programs.swaylock.settings` was non-empty. Users with
|
||||
`home.stateVersion` set to earlier versions will continue to get the
|
||||
old implicit behavior.
|
||||
|
||||
@@ -27,4 +27,8 @@ The state version in this release includes the changes below. These
|
||||
changes are only active if the `home.stateVersion` option is set to
|
||||
\"25.05\" or later.
|
||||
|
||||
- No changes.
|
||||
- The [](#opt-programs.git.signing.format) option no longer defaults to
|
||||
`"openpgp"`. Users who use Git signing with GPG should explicitly set
|
||||
this option to `"openpgp"` to maintain the previous behavior. Users
|
||||
with `home.stateVersion` set to earlier versions will continue to get
|
||||
the `"openpgp"` default for backwards compatibility.
|
||||
|
||||
@@ -80,3 +80,10 @@ changes are only active if the `home.stateVersion` option is set to
|
||||
`{ PASSWORD_STORE_DIR = $XDG_DATA_HOME/password-store; }` anymore by its
|
||||
default value. This will revert to the default behaviour of the program,
|
||||
namely `$HOME/.password-store` to be used as the store path.
|
||||
|
||||
- On macOS, [](#opt-targets.darwin.copyApps.enable) is now enabled by
|
||||
default instead of [](#opt-targets.darwin.linkApps.enable). This means
|
||||
applications from `home.packages` will be copied to
|
||||
`~/Applications/Home Manager Apps` rather than symlinked, making them
|
||||
work properly with Spotlight. Users with `home.stateVersion` set to
|
||||
earlier versions will continue to use `linkApps` by default.
|
||||
|
||||
@@ -15,3 +15,7 @@ changes are only active if the `home.stateVersion` option is set to
|
||||
|
||||
- The `gtk.gtk4.theme` option does not mirror `gtk.theme` by default
|
||||
anymore.
|
||||
|
||||
- The `programs.zsh.dotDir` option now defaults to the XDG configuration
|
||||
directory (usually `~/.config/zsh`) when `xdg.enable` is true.
|
||||
|
||||
|
||||
6
flake.lock
generated
6
flake.lock
generated
@@ -2,11 +2,11 @@
|
||||
"nodes": {
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1765472234,
|
||||
"narHash": "sha256-9VvC20PJPsleGMewwcWYKGzDIyjckEz8uWmT0vCDYK0=",
|
||||
"lastModified": 1766651565,
|
||||
"narHash": "sha256-QEhk0eXgyIqTpJ/ehZKg9IKS7EtlWxF3N7DXy42zPfU=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "2fbfb1d73d239d2402a8fe03963e37aab15abe8b",
|
||||
"rev": "3e2499d5539c16d0d173ba53552a4ff8547f4539",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
||||
20
modules/misc/news/2025/12/2025-12-27_13-32-14.nix
Normal file
20
modules/misc/news/2025/12/2025-12-27_13-32-14.nix
Normal file
@@ -0,0 +1,20 @@
|
||||
{ config, ... }:
|
||||
{
|
||||
time = "2025-12-27T19:00:00+00:00";
|
||||
condition = config.programs.zsh.enable;
|
||||
|
||||
message = ''
|
||||
The default value of `programs.zsh.dotDir` has changed.
|
||||
|
||||
When `home.stateVersion` is set to "26.05" or later, and `xdg.enable` is
|
||||
`true` (the default), `programs.zsh.dotDir` now defaults to
|
||||
`''${config.xdg.configHome}/zsh`. Previously, it defaulted to the home
|
||||
directory.
|
||||
|
||||
This means your Zsh configuration files (`.zshrc`, `.zshenv`, etc.) will be
|
||||
moved to `~/.config/zsh` (or your configured XDG config home).
|
||||
|
||||
If you prefer the old behavior, you can explicitly set:
|
||||
`programs.zsh.dotDir = config.home.homeDirectory;`
|
||||
'';
|
||||
}
|
||||
@@ -185,7 +185,7 @@ in
|
||||
|
||||
extraCss = mkOption {
|
||||
type = nullOr lines;
|
||||
default = "";
|
||||
default = null;
|
||||
description = ''
|
||||
Extra CSS lines to add to {file}`~/.config/anyrun/style.css`.
|
||||
'';
|
||||
|
||||
@@ -197,6 +197,47 @@ in
|
||||
};
|
||||
};
|
||||
|
||||
rules = lib.mkOption {
|
||||
type = lib.types.attrsOf (lib.types.either lib.types.lines lib.types.path);
|
||||
default = { };
|
||||
description = ''
|
||||
Modular rule files for Claude Code.
|
||||
The attribute name becomes the rule filename, and the value is either:
|
||||
- Inline content as a string
|
||||
- A path to a file containing the rule content
|
||||
Rules are stored in .claude/rules/ directory.
|
||||
All markdown files in .claude/rules/ are automatically loaded as project memory.
|
||||
'';
|
||||
example = lib.literalExpression ''
|
||||
{
|
||||
code-style = '''
|
||||
# Code Style Guidelines
|
||||
|
||||
- Use consistent formatting
|
||||
- Follow language conventions
|
||||
''';
|
||||
testing = '''
|
||||
# Testing Conventions
|
||||
|
||||
- Write tests for all new features
|
||||
- Maintain test coverage above 80%
|
||||
''';
|
||||
security = ./rules/security.md;
|
||||
}
|
||||
'';
|
||||
};
|
||||
|
||||
rulesDir = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.path;
|
||||
default = null;
|
||||
description = ''
|
||||
Path to a directory containing rule files for Claude Code.
|
||||
Rule files from this directory will be symlinked to .claude/rules/.
|
||||
All markdown files in this directory are automatically loaded as project memory.
|
||||
'';
|
||||
example = lib.literalExpression "./rules";
|
||||
};
|
||||
|
||||
agentsDir = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.path;
|
||||
default = null;
|
||||
@@ -325,6 +366,10 @@ in
|
||||
assertion = !(cfg.memory.text != null && cfg.memory.source != null);
|
||||
message = "Cannot specify both `programs.claude-code.memory.text` and `programs.claude-code.memory.source`";
|
||||
}
|
||||
{
|
||||
assertion = !(cfg.rules != { } && cfg.rulesDir != null);
|
||||
message = "Cannot specify both `programs.claude-code.rules` and `programs.claude-code.rulesDir`";
|
||||
}
|
||||
{
|
||||
assertion = !(cfg.agents != { } && cfg.agentsDir != null);
|
||||
message = "Cannot specify both `programs.claude-code.agents` and `programs.claude-code.agentsDir`";
|
||||
@@ -386,6 +431,11 @@ in
|
||||
if cfg.memory.text != null then { text = cfg.memory.text; } else { source = cfg.memory.source; }
|
||||
);
|
||||
|
||||
".claude/rules" = lib.mkIf (cfg.rulesDir != null) {
|
||||
source = cfg.rulesDir;
|
||||
recursive = true;
|
||||
};
|
||||
|
||||
".claude/agents" = lib.mkIf (cfg.agentsDir != null) {
|
||||
source = cfg.agentsDir;
|
||||
recursive = true;
|
||||
@@ -406,6 +456,12 @@ in
|
||||
recursive = true;
|
||||
};
|
||||
}
|
||||
// lib.mapAttrs' (
|
||||
name: content:
|
||||
lib.nameValuePair ".claude/rules/${name}.md" (
|
||||
if lib.isPath content then { source = content; } else { text = content; }
|
||||
)
|
||||
) cfg.rules
|
||||
// lib.mapAttrs' (
|
||||
name: content:
|
||||
lib.nameValuePair ".claude/agents/${name}.md" (
|
||||
|
||||
@@ -21,14 +21,19 @@ in
|
||||
settings = lib.mkOption {
|
||||
inherit (jsonFormat) type;
|
||||
default = { };
|
||||
example = lib.literalExpression ''
|
||||
{
|
||||
"theme": "Default",
|
||||
"vimMode": true,
|
||||
"preferredEditor": "nvim",
|
||||
"autoAccept": true
|
||||
}
|
||||
'';
|
||||
example = {
|
||||
ui.theme = "Default";
|
||||
general = {
|
||||
vimMode = true;
|
||||
preferredEditor = "nvim";
|
||||
previewFeatures = true;
|
||||
};
|
||||
ide.enabled = true;
|
||||
privacy.usageStatisticsEnabled = false;
|
||||
tools.autoAccept = false;
|
||||
context.loadMemoryFromIncludeDirectories = true;
|
||||
security.auth.selectedType = "oauth-personal";
|
||||
};
|
||||
description = "JSON config for gemini-cli";
|
||||
};
|
||||
|
||||
@@ -81,12 +86,12 @@ in
|
||||
};
|
||||
|
||||
defaultModel = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "gemini-2.5-pro";
|
||||
type = lib.types.nullOr lib.types.str;
|
||||
default = null;
|
||||
example = "gemini-2.5-flash";
|
||||
description = ''
|
||||
The default model to use for the CLI.
|
||||
Will be set as $GEMINI_MODEL.
|
||||
Will be set as $GEMINI_MODEL when configured.
|
||||
'';
|
||||
};
|
||||
|
||||
@@ -138,7 +143,9 @@ in
|
||||
file.".gemini/settings.json" = lib.mkIf (cfg.settings != { }) {
|
||||
source = jsonFormat.generate "gemini-cli-settings.json" cfg.settings;
|
||||
};
|
||||
sessionVariables.GEMINI_MODEL = cfg.defaultModel;
|
||||
sessionVariables = lib.mkIf (cfg.defaultModel != null) {
|
||||
GEMINI_MODEL = cfg.defaultModel;
|
||||
};
|
||||
};
|
||||
}
|
||||
{
|
||||
|
||||
@@ -24,7 +24,8 @@ let
|
||||
meliAccounts = (lib.attrsets.mapAttrs (name: value: (mkMeliAccounts name value)) enabledAccounts);
|
||||
|
||||
mkMeliAccounts = (
|
||||
name: account: {
|
||||
name: account:
|
||||
{
|
||||
root_mailbox = "${config.accounts.email.maildirBasePath}/${account.maildir.path}";
|
||||
format = "Maildir";
|
||||
identity = account.address;
|
||||
@@ -33,6 +34,7 @@ let
|
||||
send_mail = mkSmtp account;
|
||||
mailboxes = account.meli.mailboxAliases;
|
||||
}
|
||||
// account.meli.settings
|
||||
);
|
||||
|
||||
mkSmtp = account: {
|
||||
@@ -153,6 +155,14 @@ in
|
||||
};
|
||||
description = "Folder display name";
|
||||
};
|
||||
|
||||
settings = mkOption {
|
||||
type = types.submodule {
|
||||
freeformType = tomlFormat.type;
|
||||
};
|
||||
default = { };
|
||||
description = "Account specific meli configuration";
|
||||
};
|
||||
};
|
||||
}
|
||||
)
|
||||
@@ -160,6 +170,15 @@ in
|
||||
};
|
||||
};
|
||||
config = mkIf config.programs.meli.enable {
|
||||
assertions = [
|
||||
{
|
||||
assertion = cfg.settings ? accounts == false;
|
||||
message = ''
|
||||
programs.meli.settings.accounts override the accounts.email values.
|
||||
Use per-email accounts.email.<ACCOUNT>.meli.settings instead'';
|
||||
}
|
||||
];
|
||||
|
||||
home.packages = [ config.programs.meli.package ];
|
||||
|
||||
# Generate meli configuration from email accounts
|
||||
|
||||
@@ -7,121 +7,46 @@
|
||||
|
||||
let
|
||||
inherit (lib)
|
||||
concatMapStringsSep
|
||||
literalExpression
|
||||
mkEnableOption
|
||||
mkIf
|
||||
mkOption
|
||||
mkRemovedOptionModule
|
||||
mkPackageOption
|
||||
optionals
|
||||
types
|
||||
;
|
||||
|
||||
cfg = config.programs.neovim;
|
||||
|
||||
fileType =
|
||||
(import ../lib/file-type.nix {
|
||||
inherit (config.home) homeDirectory;
|
||||
inherit lib pkgs;
|
||||
}).fileType;
|
||||
inherit
|
||||
(
|
||||
(import ../lib/file-type.nix {
|
||||
inherit (config.home) homeDirectory;
|
||||
inherit lib pkgs;
|
||||
})
|
||||
)
|
||||
fileType
|
||||
;
|
||||
|
||||
jsonFormat = pkgs.formats.json { };
|
||||
|
||||
pluginWithConfigType = types.submodule {
|
||||
options = {
|
||||
config = mkOption {
|
||||
type = types.nullOr types.lines;
|
||||
description = "Script to configure this plugin. The scripting language should match type.";
|
||||
default = null;
|
||||
};
|
||||
|
||||
type = mkOption {
|
||||
type = types.either (types.enum [
|
||||
"lua"
|
||||
"viml"
|
||||
"teal"
|
||||
"fennel"
|
||||
]) types.str;
|
||||
description = "Language used in config. Configurations are aggregated per-language.";
|
||||
default = "viml";
|
||||
};
|
||||
|
||||
optional = mkEnableOption "optional" // {
|
||||
description = "Don't load by default (load with :packadd)";
|
||||
};
|
||||
|
||||
plugin = lib.mkPackageOption pkgs.vimPlugins "plugin" {
|
||||
default = null;
|
||||
example = "pkgs.vimPlugins.nvim-treesitter";
|
||||
pkgsText = "pkgs.vimPlugins";
|
||||
};
|
||||
|
||||
runtime = mkOption {
|
||||
default = { };
|
||||
# passing actual "${xdg.configHome}/nvim" as basePath was a bit tricky
|
||||
# due to how fileType.target is implemented
|
||||
type = fileType "programs.neovim.plugins._.runtime" "{var}`xdg.configHome/nvim`" "nvim";
|
||||
example = literalExpression ''
|
||||
{ "ftplugin/c.vim".text = "setlocal omnifunc=v:lua.vim.lsp.omnifunc"; }
|
||||
'';
|
||||
description = ''
|
||||
Set of files that have to be linked in nvim config folder.
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
allPlugins =
|
||||
cfg.plugins
|
||||
++ lib.optional cfg.coc.enable {
|
||||
type = "viml";
|
||||
plugin = cfg.coc.package;
|
||||
config = cfg.coc.pluginConfig;
|
||||
optional = false;
|
||||
};
|
||||
|
||||
luaPackages = cfg.finalPackage.unwrapped.lua.pkgs;
|
||||
resolvedExtraLuaPackages = cfg.extraLuaPackages luaPackages;
|
||||
|
||||
extraMakeWrapperArgs = lib.optionalString (
|
||||
cfg.extraPackages != [ ]
|
||||
) ''--suffix PATH : "${lib.makeBinPath cfg.extraPackages}"'';
|
||||
extraMakeWrapperLuaCArgs =
|
||||
lib.optionalString (resolvedExtraLuaPackages != [ ])
|
||||
''--suffix LUA_CPATH ";" "${
|
||||
lib.concatMapStringsSep ";" luaPackages.getLuaCPath resolvedExtraLuaPackages
|
||||
}"'';
|
||||
extraMakeWrapperLuaArgs =
|
||||
lib.optionalString (resolvedExtraLuaPackages != [ ])
|
||||
''--suffix LUA_PATH ";" "${
|
||||
lib.concatMapStringsSep ";" luaPackages.getLuaPath resolvedExtraLuaPackages
|
||||
}"'';
|
||||
in
|
||||
{
|
||||
meta.maintainers = with lib.maintainers; [ khaneliman ];
|
||||
|
||||
imports = [
|
||||
(mkRemovedOptionModule [
|
||||
"programs"
|
||||
"neovim"
|
||||
"withPython"
|
||||
] "Python2 support has been removed from neovim.")
|
||||
(mkRemovedOptionModule [
|
||||
"programs"
|
||||
"neovim"
|
||||
"extraPythonPackages"
|
||||
] "Python2 support has been removed from neovim.")
|
||||
(mkRemovedOptionModule [ "programs" "neovim" "configure" ] ''
|
||||
programs.neovim.configure is deprecated.
|
||||
Other programs.neovim options can override its settings or ignore them.
|
||||
Please use the other options at your disposal:
|
||||
configure.packages.*.opt -> programs.neovim.plugins = [ { plugin = ...; optional = true; }]
|
||||
configure.packages.*.start -> programs.neovim.plugins = [ { plugin = ...; }]
|
||||
configure.customRC -> programs.neovim.extraConfig
|
||||
'')
|
||||
];
|
||||
options = {
|
||||
programs.neovim = {
|
||||
enable = mkEnableOption "Neovim";
|
||||
|
||||
package = mkPackageOption pkgs "neovim" { default = "neovim-unwrapped"; };
|
||||
|
||||
finalPackage = mkOption {
|
||||
type = types.package;
|
||||
readOnly = true;
|
||||
description = "Resulting customized neovim package.";
|
||||
};
|
||||
|
||||
# Aliases
|
||||
viAlias = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
@@ -146,6 +71,17 @@ in
|
||||
'';
|
||||
};
|
||||
|
||||
defaultEditor = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = ''
|
||||
Whether to configure {command}`nvim` as the default
|
||||
editor using the {env}`EDITOR` and {env}`VISUAL`
|
||||
environment variables.
|
||||
'';
|
||||
};
|
||||
|
||||
# Providers & Runtimes
|
||||
withNodeJs = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
@@ -155,11 +91,12 @@ in
|
||||
'';
|
||||
};
|
||||
|
||||
withRuby = mkOption {
|
||||
type = types.nullOr types.bool;
|
||||
default = true;
|
||||
withPerl = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = ''
|
||||
Enable ruby provider.
|
||||
Enable perl provider. Set to `true` to
|
||||
use Perl plugins.
|
||||
'';
|
||||
};
|
||||
|
||||
@@ -172,21 +109,16 @@ in
|
||||
'';
|
||||
};
|
||||
|
||||
withRuby = mkOption {
|
||||
type = types.nullOr types.bool;
|
||||
default = true;
|
||||
description = ''
|
||||
Enable ruby provider.
|
||||
'';
|
||||
};
|
||||
|
||||
extraPython3Packages = mkOption {
|
||||
# In case we get a plain list, we need to turn it into a function,
|
||||
# as expected by the function in nixpkgs.
|
||||
# The only way to do so is to call `const`, which will ignore its input.
|
||||
type =
|
||||
let
|
||||
fromType = types.listOf types.package;
|
||||
in
|
||||
types.coercedTo fromType (lib.flip lib.warn lib.const ''
|
||||
Assigning a plain list to extraPython3Packages is deprecated.
|
||||
Please assign a function taking a package set as argument, so
|
||||
extraPython3Packages = [ pkgs.python3Packages.xxx ];
|
||||
should become
|
||||
extraPython3Packages = ps: [ ps.xxx ];
|
||||
'') (types.functionTo fromType);
|
||||
type = types.functionTo (types.listOf types.package);
|
||||
default = _: [ ];
|
||||
defaultText = literalExpression "ps: [ ]";
|
||||
example = literalExpression "pyPkgs: with pyPkgs; [ python-language-server ]";
|
||||
@@ -198,21 +130,8 @@ in
|
||||
'';
|
||||
};
|
||||
|
||||
# We get the Lua package from the final package and use its
|
||||
# Lua packageset to evaluate the function that this option was set to.
|
||||
# This ensures that we always use the same Lua version as the Neovim package.
|
||||
extraLuaPackages = mkOption {
|
||||
type =
|
||||
let
|
||||
fromType = types.listOf types.package;
|
||||
in
|
||||
types.coercedTo fromType (lib.flip lib.warn lib.const ''
|
||||
Assigning a plain list to extraLuaPackages is deprecated.
|
||||
Please assign a function taking a package set as argument, so
|
||||
extraLuaPackages = [ pkgs.lua51Packages.xxx ];
|
||||
should become
|
||||
extraLuaPackages = ps: [ ps.xxx ];
|
||||
'') (types.functionTo fromType);
|
||||
type = types.functionTo (types.listOf types.package);
|
||||
default = _: [ ];
|
||||
defaultText = literalExpression "ps: [ ]";
|
||||
example = literalExpression "luaPkgs: with luaPkgs; [ luautf8 ]";
|
||||
@@ -224,8 +143,34 @@ in
|
||||
'';
|
||||
};
|
||||
|
||||
# Wrapper Configuration
|
||||
extraName = mkOption {
|
||||
type = types.str;
|
||||
default = "";
|
||||
description = ''
|
||||
Extra name appended to the wrapper package name.
|
||||
'';
|
||||
};
|
||||
|
||||
autowrapRuntimeDeps = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = ''
|
||||
Whether to automatically wrap the binary with the runtime dependencies of the plugins.
|
||||
'';
|
||||
};
|
||||
|
||||
waylandSupport = mkOption {
|
||||
type = types.bool;
|
||||
default = pkgs.stdenv.isLinux;
|
||||
defaultText = literalExpression "pkgs.stdenv.isLinux";
|
||||
description = ''
|
||||
Whether to enable Wayland clipboard support.
|
||||
'';
|
||||
};
|
||||
|
||||
extraWrapperArgs = mkOption {
|
||||
type = with types; listOf str;
|
||||
type = types.listOf types.str;
|
||||
default = [ ];
|
||||
example = literalExpression ''
|
||||
[
|
||||
@@ -246,54 +191,14 @@ in
|
||||
'';
|
||||
};
|
||||
|
||||
generatedConfigViml = mkOption {
|
||||
type = types.lines;
|
||||
visible = true;
|
||||
readOnly = true;
|
||||
description = ''
|
||||
Generated vimscript config.
|
||||
'';
|
||||
};
|
||||
|
||||
generatedConfigs = mkOption {
|
||||
type = types.attrsOf types.lines;
|
||||
visible = true;
|
||||
readOnly = true;
|
||||
example = literalExpression ''
|
||||
{
|
||||
viml = '''
|
||||
" Generated by home-manager
|
||||
map <leader> ,
|
||||
''';
|
||||
|
||||
lua = '''
|
||||
-- Generated by home-manager
|
||||
vim.opt.background = "dark"
|
||||
''';
|
||||
}'';
|
||||
description = ''
|
||||
Generated configurations with as key their language (set via type).
|
||||
'';
|
||||
};
|
||||
|
||||
package = lib.mkPackageOption pkgs "neovim" { default = "neovim-unwrapped"; };
|
||||
|
||||
finalPackage = mkOption {
|
||||
type = types.package;
|
||||
readOnly = true;
|
||||
description = "Resulting customized neovim package.";
|
||||
};
|
||||
|
||||
defaultEditor = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = ''
|
||||
Whether to configure {command}`nvim` as the default
|
||||
editor using the {env}`EDITOR` and {env}`VISUAL`
|
||||
environment variables.
|
||||
'';
|
||||
extraPackages = mkOption {
|
||||
type = types.listOf types.package;
|
||||
default = [ ];
|
||||
example = literalExpression "[ pkgs.shfmt ]";
|
||||
description = "Extra packages available to nvim.";
|
||||
};
|
||||
|
||||
# Configuration & Plugins
|
||||
extraConfig = mkOption {
|
||||
type = types.lines;
|
||||
default = "";
|
||||
@@ -308,45 +213,92 @@ in
|
||||
extraLuaConfig = mkOption {
|
||||
type = types.lines;
|
||||
default = "";
|
||||
example = ''
|
||||
vim.opt.nobackup = true
|
||||
example = lib.literalExpression ''
|
||||
let
|
||||
nvimEarlyInit = lib.mkOrder 500 "set rtp+=vim.opt.rtp:prepend('/home/user/myplugin')";
|
||||
nvimLateInit = lib.mkAfter 1000 "vim.opt.signcolumn = 'auto:1-3'";
|
||||
in
|
||||
lib.mkMerge [ nvimEarlyInit nvimLateInit ];
|
||||
'';
|
||||
description = ''
|
||||
Custom lua lines.
|
||||
Content to be added to {file}`init.lua`.
|
||||
|
||||
To specify the order, use `lib.mkOrder`, `lib.mkBefore`, `lib.mkAfter`.
|
||||
'';
|
||||
};
|
||||
|
||||
extraPackages = mkOption {
|
||||
type = with types; listOf package;
|
||||
default = [ ];
|
||||
example = literalExpression "[ pkgs.shfmt ]";
|
||||
description = "Extra packages available to nvim.";
|
||||
};
|
||||
plugins =
|
||||
let
|
||||
pluginWithConfigType = types.submodule {
|
||||
options = {
|
||||
config = mkOption {
|
||||
type = types.nullOr types.lines;
|
||||
description = "Script to configure this plugin. The scripting language should match type.";
|
||||
default = null;
|
||||
};
|
||||
|
||||
plugins = mkOption {
|
||||
type = with types; listOf (either package pluginWithConfigType);
|
||||
default = [ ];
|
||||
example = literalExpression ''
|
||||
with pkgs.vimPlugins; [
|
||||
yankring
|
||||
vim-nix
|
||||
{ plugin = vim-startify;
|
||||
config = "let g:startify_change_to_vcs_root = 0";
|
||||
}
|
||||
]
|
||||
'';
|
||||
description = ''
|
||||
List of vim plugins to install optionally associated with
|
||||
configuration to be placed in init.vim.
|
||||
type = mkOption {
|
||||
type = types.either (types.enum [
|
||||
"lua"
|
||||
"viml"
|
||||
"teal"
|
||||
"fennel"
|
||||
]) types.str;
|
||||
description = "Language used in config. Configurations are aggregated per-language.";
|
||||
default = "viml";
|
||||
};
|
||||
|
||||
This option is mutually exclusive with {var}`configure`.
|
||||
'';
|
||||
};
|
||||
optional = mkEnableOption "optional" // {
|
||||
description = "Don't load by default (load with :packadd)";
|
||||
};
|
||||
|
||||
plugin = mkPackageOption pkgs.vimPlugins "plugin" {
|
||||
default = null;
|
||||
example = "pkgs.vimPlugins.nvim-treesitter";
|
||||
pkgsText = "pkgs.vimPlugins";
|
||||
};
|
||||
|
||||
runtime = mkOption {
|
||||
default = { };
|
||||
# passing actual "${xdg.configHome}/nvim" as basePath was a bit tricky
|
||||
# due to how fileType.target is implemented
|
||||
type = fileType "programs.neovim.plugins._.runtime" "{var}`xdg.configHome/nvim`" "nvim";
|
||||
example = literalExpression ''
|
||||
{ "ftplugin/c.vim".text = "setlocal omnifunc=v:lua.vim.lsp.omnifunc"; }
|
||||
'';
|
||||
description = ''
|
||||
Set of files that have to be linked in nvim config folder.
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
in
|
||||
mkOption {
|
||||
type = types.listOf (types.either types.package pluginWithConfigType);
|
||||
default = [ ];
|
||||
example = literalExpression ''
|
||||
with pkgs.vimPlugins;
|
||||
[
|
||||
yankring
|
||||
vim-nix
|
||||
{ plugin = vim-startify;
|
||||
config = "let g:startify_change_to_vcs_root = 0";
|
||||
}
|
||||
]
|
||||
'';
|
||||
description = ''
|
||||
List of vim plugins to install optionally associated with
|
||||
configuration to be placed in init.vim.
|
||||
|
||||
This option is mutually exclusive with {var}`configure`.
|
||||
'';
|
||||
};
|
||||
|
||||
coc = {
|
||||
enable = mkEnableOption "Coc";
|
||||
|
||||
package = lib.mkPackageOption pkgs "coc-nvim" {
|
||||
package = mkPackageOption pkgs "coc-nvim" {
|
||||
default = [
|
||||
"vimPlugins"
|
||||
"coc-nvim"
|
||||
@@ -376,7 +328,7 @@ in
|
||||
filetypes = [ "haskell" "lhaskell" ];
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
'';
|
||||
description = ''
|
||||
Extra configuration lines to add to
|
||||
@@ -393,11 +345,52 @@ in
|
||||
description = "Script to configure CoC. Must be viml.";
|
||||
};
|
||||
};
|
||||
|
||||
# Generated / Read-Only
|
||||
generatedConfigViml = mkOption {
|
||||
type = types.lines;
|
||||
visible = true;
|
||||
readOnly = true;
|
||||
description = ''
|
||||
Generated vimscript config.
|
||||
'';
|
||||
};
|
||||
|
||||
generatedConfigs = mkOption {
|
||||
type = types.attrsOf types.lines;
|
||||
visible = true;
|
||||
readOnly = true;
|
||||
example = literalExpression ''
|
||||
{
|
||||
viml = '''
|
||||
" Generated by home-manager
|
||||
map <leader> ,
|
||||
''';
|
||||
|
||||
lua = '''
|
||||
-- Generated by home-manager
|
||||
vim.opt.background = "dark"
|
||||
''';
|
||||
}
|
||||
'';
|
||||
description = ''
|
||||
Generated configurations with as key their language (set via type).
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
config =
|
||||
config = mkIf cfg.enable (
|
||||
let
|
||||
allPlugins =
|
||||
cfg.plugins
|
||||
++ lib.optional cfg.coc.enable {
|
||||
type = "viml";
|
||||
plugin = cfg.coc.package;
|
||||
config = cfg.coc.pluginConfig;
|
||||
optional = false;
|
||||
};
|
||||
|
||||
defaultPlugin = {
|
||||
type = "viml";
|
||||
plugin = null;
|
||||
@@ -413,81 +406,100 @@ in
|
||||
|
||||
suppressNotVimlConfig = p: if p.type != "viml" then p // { config = null; } else p;
|
||||
|
||||
neovimConfig = pkgs.neovimUtils.makeNeovimConfig {
|
||||
# Lua & Python Package Resolution
|
||||
luaPackages = cfg.finalPackage.unwrapped.lua.pkgs;
|
||||
resolvedExtraLuaPackages = cfg.extraLuaPackages luaPackages;
|
||||
|
||||
# Wrapper Arguments Construction
|
||||
extraMakeWrapperArgs = optionals (cfg.extraPackages != [ ]) [
|
||||
"--suffix"
|
||||
"PATH"
|
||||
":"
|
||||
(lib.makeBinPath cfg.extraPackages)
|
||||
];
|
||||
|
||||
extraMakeWrapperLuaCArgs = optionals (resolvedExtraLuaPackages != [ ]) [
|
||||
"--suffix"
|
||||
"LUA_CPATH"
|
||||
";"
|
||||
(concatMapStringsSep ";" luaPackages.getLuaCPath resolvedExtraLuaPackages)
|
||||
];
|
||||
|
||||
extraMakeWrapperLuaArgs = optionals (resolvedExtraLuaPackages != [ ]) [
|
||||
"--suffix"
|
||||
"LUA_PATH"
|
||||
";"
|
||||
(concatMapStringsSep ";" luaPackages.getLuaPath resolvedExtraLuaPackages)
|
||||
];
|
||||
|
||||
wrappedNeovim' = pkgs.wrapNeovimUnstable cfg.package {
|
||||
withNodeJs = cfg.withNodeJs || cfg.coc.enable;
|
||||
plugins = map suppressNotVimlConfig pluginsNormalized;
|
||||
|
||||
inherit (cfg)
|
||||
extraPython3Packages
|
||||
withPython3
|
||||
withRuby
|
||||
withPerl
|
||||
viAlias
|
||||
vimAlias
|
||||
extraName
|
||||
autowrapRuntimeDeps
|
||||
waylandSupport
|
||||
;
|
||||
withNodeJs = cfg.withNodeJs || cfg.coc.enable;
|
||||
plugins = map suppressNotVimlConfig pluginsNormalized;
|
||||
customRC = cfg.extraConfig;
|
||||
neovimRcContent = cfg.extraConfig;
|
||||
wrapperArgs =
|
||||
cfg.extraWrapperArgs ++ extraMakeWrapperArgs ++ extraMakeWrapperLuaCArgs ++ extraMakeWrapperLuaArgs;
|
||||
wrapRc = false;
|
||||
};
|
||||
|
||||
wrappedNeovim' = pkgs.wrapNeovimUnstable cfg.package (
|
||||
neovimConfig
|
||||
// {
|
||||
wrapperArgs =
|
||||
(lib.escapeShellArgs (neovimConfig.wrapperArgs ++ cfg.extraWrapperArgs))
|
||||
+ " "
|
||||
+ extraMakeWrapperArgs
|
||||
+ " "
|
||||
+ extraMakeWrapperLuaCArgs
|
||||
+ " "
|
||||
+ extraMakeWrapperLuaArgs;
|
||||
wrapRc = false;
|
||||
}
|
||||
);
|
||||
in
|
||||
mkIf cfg.enable {
|
||||
{
|
||||
programs.neovim = {
|
||||
generatedConfigViml = cfg.extraConfig;
|
||||
|
||||
programs.neovim.generatedConfigViml = neovimConfig.neovimRcContent;
|
||||
generatedConfigs =
|
||||
let
|
||||
grouped = builtins.groupBy (x: x.type) pluginsNormalized;
|
||||
configsOnly = lib.foldl (acc: p: if p.config != null then acc ++ [ p.config ] else acc) [ ];
|
||||
in
|
||||
lib.mapAttrs (_name: vals: lib.concatStringsSep "\n" (configsOnly vals)) grouped;
|
||||
|
||||
programs.neovim.generatedConfigs =
|
||||
let
|
||||
grouped = lib.lists.groupBy (x: x.type) pluginsNormalized;
|
||||
configsOnly = lib.foldl (acc: p: if p.config != null then acc ++ [ p.config ] else acc) [ ];
|
||||
in
|
||||
lib.mapAttrs (name: vals: lib.concatStringsSep "\n" (configsOnly vals)) grouped;
|
||||
|
||||
home.packages = [ cfg.finalPackage ];
|
||||
|
||||
home.sessionVariables = mkIf cfg.defaultEditor {
|
||||
EDITOR = "nvim";
|
||||
VISUAL = "nvim";
|
||||
finalPackage = wrappedNeovim';
|
||||
};
|
||||
|
||||
home.shellAliases = mkIf cfg.vimdiffAlias { vimdiff = "nvim -d"; };
|
||||
home = {
|
||||
packages = [ cfg.finalPackage ];
|
||||
|
||||
xdg.configFile =
|
||||
let
|
||||
hasLuaConfig = lib.hasAttr "lua" config.programs.neovim.generatedConfigs;
|
||||
in
|
||||
lib.mkMerge (
|
||||
# writes runtime
|
||||
(map (x: x.runtime) pluginsNormalized)
|
||||
++ [
|
||||
{
|
||||
"nvim/init.lua" =
|
||||
let
|
||||
luaRcContent =
|
||||
lib.optionalString (
|
||||
wrappedNeovim'.initRc != ""
|
||||
) "vim.cmd [[source ${pkgs.writeText "nvim-init-home-manager.vim" wrappedNeovim'.initRc}]]\n"
|
||||
+ config.programs.neovim.extraLuaConfig
|
||||
+ lib.optionalString hasLuaConfig config.programs.neovim.generatedConfigs.lua;
|
||||
in
|
||||
mkIf (luaRcContent != "") { text = luaRcContent; };
|
||||
sessionVariables = mkIf cfg.defaultEditor {
|
||||
EDITOR = "nvim";
|
||||
VISUAL = "nvim";
|
||||
};
|
||||
|
||||
"nvim/coc-settings.json" = mkIf cfg.coc.enable {
|
||||
source = jsonFormat.generate "coc-settings.json" cfg.coc.settings;
|
||||
};
|
||||
}
|
||||
]
|
||||
);
|
||||
shellAliases = mkIf cfg.vimdiffAlias { vimdiff = "nvim -d"; };
|
||||
};
|
||||
|
||||
programs.neovim.finalPackage = wrappedNeovim';
|
||||
};
|
||||
programs.neovim.extraLuaConfig = lib.mkMerge [
|
||||
(lib.mkIf (wrappedNeovim'.initRc != "") (
|
||||
lib.mkBefore "vim.cmd [[source ${pkgs.writeText "nvim-init-home-manager.vim" wrappedNeovim'.initRc}]]"
|
||||
))
|
||||
(lib.mkIf (lib.hasAttr "lua" cfg.generatedConfigs) (lib.mkAfter cfg.generatedConfigs.lua))
|
||||
];
|
||||
|
||||
xdg.configFile = lib.mkMerge (
|
||||
# writes runtime
|
||||
(map (x: x.runtime) pluginsNormalized)
|
||||
++ [
|
||||
{
|
||||
"nvim/init.lua" = mkIf (cfg.extraLuaConfig != "") {
|
||||
text = cfg.extraLuaConfig;
|
||||
};
|
||||
|
||||
"nvim/coc-settings.json" = mkIf cfg.coc.enable {
|
||||
source = jsonFormat.generate "coc-settings.json" cfg.coc.settings;
|
||||
};
|
||||
}
|
||||
]
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -30,44 +30,42 @@ let
|
||||
mapAttrsToList (name: value: ''${name}="${lib.escape [ ''"'' "\\" ] (toString value)}"'') envStr
|
||||
);
|
||||
|
||||
bindOptions = {
|
||||
address = mkOption {
|
||||
type = types.str;
|
||||
default = "localhost";
|
||||
example = "example.org";
|
||||
description = "The address where to bind the port.";
|
||||
};
|
||||
|
||||
port = mkOption {
|
||||
type = types.nullOr types.port;
|
||||
default = null;
|
||||
example = 8080;
|
||||
description = "Specifies port number to bind on bind address.";
|
||||
};
|
||||
};
|
||||
|
||||
dynamicForwardModule = types.submodule { options = bindOptions; };
|
||||
|
||||
forwardModule = types.submodule {
|
||||
options = {
|
||||
bind = bindOptions;
|
||||
|
||||
host = {
|
||||
mkAddressPortModule =
|
||||
{
|
||||
actionType,
|
||||
nullableAddress ? actionType == "forward",
|
||||
}:
|
||||
types.submodule {
|
||||
options = {
|
||||
address = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
type = if nullableAddress then types.nullOr types.str else types.str;
|
||||
default = if nullableAddress then null else "localhost";
|
||||
example = "example.org";
|
||||
description = "The address where to forward the traffic to.";
|
||||
description = "The address to ${actionType} to.";
|
||||
};
|
||||
|
||||
port = mkOption {
|
||||
type = types.nullOr types.port;
|
||||
default = null;
|
||||
example = 80;
|
||||
description = "Specifies port number to forward the traffic to.";
|
||||
example = 8080;
|
||||
description = "Specifies port number to ${actionType} to.";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
dynamicForwardModule = mkAddressPortModule { actionType = "bind"; };
|
||||
|
||||
forwardModule = types.submodule {
|
||||
options = {
|
||||
bind = mkOption {
|
||||
type = mkAddressPortModule { actionType = "bind"; };
|
||||
description = "Local port binding options";
|
||||
};
|
||||
host = mkOption {
|
||||
type = mkAddressPortModule { actionType = "forward"; };
|
||||
description = "Host port binding options";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
matchBlockModule = types.submodule {
|
||||
@@ -383,6 +381,18 @@ let
|
||||
example = "10m";
|
||||
description = "Whether control socket should remain open in the background.";
|
||||
};
|
||||
|
||||
kexAlgorithms = mkOption {
|
||||
type = types.nullOr (types.listOf types.str);
|
||||
default = null;
|
||||
example = [
|
||||
"curve25519-sha256@libssh.org"
|
||||
"diffie-hellman-group-exchange-sha256"
|
||||
];
|
||||
description = ''
|
||||
Specifies the available KEX (Key Exchange) algorithms.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
# config.host = mkDefault dagName;
|
||||
@@ -430,6 +440,9 @@ let
|
||||
++ map (f: " LocalForward" + addressPort f.bind + addressPort f.host) cf.localForwards
|
||||
++ map (f: " RemoteForward" + addressPort f.bind + addressPort f.host) cf.remoteForwards
|
||||
++ map (f: " DynamicForward" + addressPort f) cf.dynamicForwards
|
||||
++ optional (
|
||||
cf.kexAlgorithms != null
|
||||
) " KexAlgorithms ${builtins.concatStringsSep "," cf.kexAlgorithms}"
|
||||
++ [
|
||||
(lib.generators.toKeyValue {
|
||||
mkKeyValue = lib.generators.mkKeyValueDefault { } " ";
|
||||
|
||||
@@ -106,13 +106,13 @@ in
|
||||
];
|
||||
|
||||
programs.bash.initExtra = lib.mkIf cfg.enableBashIntegration ''
|
||||
eval "$(${lib.getExe cfg.package} init bash)"
|
||||
source ${cfg.package}/share/television/completion.bash
|
||||
'';
|
||||
programs.zsh.initContent = lib.mkIf cfg.enableZshIntegration ''
|
||||
eval "$(${lib.getExe cfg.package} init zsh)"
|
||||
source ${cfg.package}/share/television/completion.zsh
|
||||
'';
|
||||
programs.fish.interactiveShellInit = lib.mkIf cfg.enableFishIntegration ''
|
||||
${lib.getExe cfg.package} init fish | source
|
||||
source ${cfg.package}/share/television/completion.fish
|
||||
'';
|
||||
};
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ let
|
||||
|
||||
packageVersion = if cfg.package != null then lib.getVersion cfg.package else null;
|
||||
themeIsToml = lib.versionAtLeast packageVersion "0.15.0";
|
||||
versionPost0_17 = lib.versionAtLeast packageVersion "0.17.0";
|
||||
in
|
||||
{
|
||||
meta.maintainers = [ lib.maintainers.leiserfg ];
|
||||
@@ -43,7 +44,12 @@ in
|
||||
useLayerShell = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = true;
|
||||
description = "If vicinae should use the layer shell";
|
||||
description = ''
|
||||
Whether vicinae should use the layer shell.
|
||||
If you are using version 0.17 or newer, you should use
|
||||
{option}.programs.vicinae.settings.launcher_window.layer_shell.enabled = false
|
||||
instead.
|
||||
'';
|
||||
};
|
||||
|
||||
extensions = lib.mkOption {
|
||||
@@ -127,7 +133,7 @@ in
|
||||
settings = lib.mkOption {
|
||||
inherit (jsonFormat) type;
|
||||
default = { };
|
||||
description = "Settings written as JSON to `~/.config/vicinae/vicinae.json.";
|
||||
description = "Settings written as JSON to `~/.config/vicinae/settings.json.";
|
||||
example = lib.literalExpression ''
|
||||
{
|
||||
faviconService = "twenty";
|
||||
@@ -158,6 +164,10 @@ in
|
||||
assertion = cfg.systemd.enable -> cfg.package != null;
|
||||
message = "{option}programs.vicinae.systemd.enable requires non null {option}programs.vicinae.package";
|
||||
}
|
||||
{
|
||||
assertion = !cfg.useLayerShell -> !versionPost0_17;
|
||||
message = ''After version 0.17, if you want to explicitly disable the use of layer shell, you need to set {option}.programs.vicinae.settings.launcher_window.layer_shell.enabled = false.'';
|
||||
}
|
||||
];
|
||||
lib.vicinae.mkExtension = (
|
||||
{
|
||||
@@ -223,10 +233,11 @@ in
|
||||
source = themeFormat.generate "vicinae-${name}-theme" theme;
|
||||
}
|
||||
) cfg.themes;
|
||||
settingsPath = if versionPost0_17 then "vicinae/settings.json" else "vicinae/vicinae.json";
|
||||
in
|
||||
{
|
||||
configFile = {
|
||||
"vicinae/vicinae.json" = lib.mkIf (cfg.settings != { }) {
|
||||
"${settingsPath}" = lib.mkIf (cfg.settings != { }) {
|
||||
source = jsonFormat.generate "vicinae-settings" cfg.settings;
|
||||
};
|
||||
}
|
||||
@@ -250,14 +261,16 @@ in
|
||||
PartOf = [ cfg.systemd.target ];
|
||||
};
|
||||
Service = {
|
||||
EnvironmentFile = pkgs.writeText "vicinae-env" ''
|
||||
USE_LAYER_SHELL=${if cfg.useLayerShell then builtins.toString 1 else builtins.toString 0}
|
||||
'';
|
||||
Type = "simple";
|
||||
ExecStart = "${lib.getExe' cfg.package "vicinae"} server";
|
||||
Restart = "always";
|
||||
RestartSec = 5;
|
||||
KillMode = "process";
|
||||
EnvironmentFile = lib.mkIf (!versionPost0_17) (
|
||||
pkgs.writeText "vicinae-env" ''
|
||||
USE_LAYER_SHELL=${if cfg.useLayerShell then builtins.toString 1 else builtins.toString 0}
|
||||
''
|
||||
);
|
||||
};
|
||||
Install = lib.mkIf cfg.systemd.autoStart {
|
||||
WantedBy = [ cfg.systemd.target ];
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
options,
|
||||
...
|
||||
}:
|
||||
let
|
||||
@@ -100,8 +101,17 @@ in
|
||||
};
|
||||
|
||||
dotDir = mkOption {
|
||||
default = homeDir;
|
||||
defaultText = "`config.home.homeDirectory`";
|
||||
default =
|
||||
if config.xdg.enable && lib.versionAtLeast config.home.stateVersion "26.05" then
|
||||
"${config.xdg.configHome}/zsh"
|
||||
else
|
||||
homeDir;
|
||||
defaultText = lib.literalExpression ''
|
||||
if config.xdg.enable && lib.versionAtLeast config.home.stateVersion "26.05" then
|
||||
"''${config.xdg.configHome}/zsh"
|
||||
else
|
||||
config.home.homeDirectory
|
||||
'';
|
||||
example = "`\${config.xdg.configHome}/zsh`";
|
||||
description = ''
|
||||
Directory where the zsh configuration and more should be located,
|
||||
@@ -391,7 +401,24 @@ in
|
||||
- config.xdg.dataHome (XDG data directory)
|
||||
- config.xdg.cacheHome (XDG cache directory)
|
||||
''
|
||||
];
|
||||
]
|
||||
++
|
||||
lib.optionals
|
||||
(
|
||||
config.xdg.enable
|
||||
&& !lib.versionAtLeast config.home.stateVersion "26.05"
|
||||
&& options.programs.zsh.dotDir.highestPrio >= 1500
|
||||
)
|
||||
[
|
||||
''
|
||||
The default value of `programs.zsh.dotDir` will change in future versions.
|
||||
You are currently using the legacy default (home directory) because `home.stateVersion` is less than "26.05".
|
||||
To silence this warning and lock in the current behavior, set:
|
||||
programs.zsh.dotDir = config.home.homeDirectory;
|
||||
To adopt the new behavior (XDG config directory), set:
|
||||
programs.zsh.dotDir = "''${config.xdg.configHome}/zsh";
|
||||
''
|
||||
];
|
||||
}
|
||||
|
||||
(mkIf (cfg.envExtra != "") {
|
||||
@@ -412,7 +439,7 @@ in
|
||||
|
||||
(mkIf (dotDirAbs != homeDir) {
|
||||
home.file."${dotDirRel}/.zshenv".text = ''
|
||||
export ZDOTDIR=${dotDirAbs}
|
||||
${config.lib.zsh.export "ZDOTDIR" dotDirAbs}
|
||||
'';
|
||||
|
||||
# When dotDir is set, only use ~/.zshenv to source ZDOTDIR/.zshenv,
|
||||
@@ -420,7 +447,7 @@ in
|
||||
# already set correctly (by e.g. spawning a zsh inside a zsh), all env
|
||||
# vars still get exported
|
||||
home.file.".zshenv".text = ''
|
||||
source ${dotDirAbs}/.zshenv
|
||||
source ${lib.escapeShellArg "${dotDirAbs}/.zshenv"}
|
||||
'';
|
||||
})
|
||||
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
{ config, lib, ... }:
|
||||
let
|
||||
cfg = config.programs.zsh;
|
||||
|
||||
stripSlash = lib.removeSuffix "/";
|
||||
in
|
||||
rec {
|
||||
homeDir = config.home.homeDirectory;
|
||||
# Raw home directory, no trailing slash.
|
||||
homeDir = stripSlash config.home.homeDirectory;
|
||||
|
||||
/*
|
||||
Escape a path string for shell usage and remove trailing slashes.
|
||||
@@ -18,10 +21,11 @@ rec {
|
||||
cleanPathStr "/path/to/dir/" => "'/path/to/dir'"
|
||||
cleanPathStr "path with spaces" => "'path with spaces'"
|
||||
*/
|
||||
cleanPathStr = pathStr: lib.escapeShellArg (lib.removeSuffix "/" pathStr);
|
||||
cleanPathStr = pathStr: lib.escapeShellArg (stripSlash pathStr);
|
||||
|
||||
/*
|
||||
Convert an absolute path to a relative path by stripping the home directory prefix.
|
||||
Returns the raw path (unescaped) for use in home.file keys.
|
||||
|
||||
This function converts absolute paths within the home directory to relative paths
|
||||
by removing the home directory prefix. Paths already relative are returned as-is.
|
||||
@@ -30,19 +34,22 @@ rec {
|
||||
Type: String -> String
|
||||
|
||||
Example:
|
||||
mkRelPathStr "/home/user/config" => "'config'"
|
||||
mkRelPathStr "config" => "'config'"
|
||||
mkRelPathStr "/home/user/config" => "config"
|
||||
mkRelPathStr "config" => "config"
|
||||
mkRelPathStr "/home/user" => "."
|
||||
mkRelPathStr "/etc/config" => <error>
|
||||
*/
|
||||
mkRelPathStr =
|
||||
pathStr:
|
||||
# is already a relative path
|
||||
if (!lib.hasPrefix "/" pathStr) then
|
||||
cleanPathStr pathStr
|
||||
# is an absolute path within home dir
|
||||
else if (lib.hasPrefix homeDir pathStr) then
|
||||
cleanPathStr (lib.removePrefix "${homeDir}/" pathStr)
|
||||
# is an absolute path not in home dir
|
||||
let
|
||||
normPath = stripSlash pathStr;
|
||||
in
|
||||
if (!lib.hasPrefix "/" normPath) then
|
||||
normPath
|
||||
else if normPath == homeDir then
|
||||
"."
|
||||
else if (lib.hasPrefix "${homeDir}/" normPath) then
|
||||
lib.removePrefix "${homeDir}/" normPath
|
||||
else
|
||||
throw ''
|
||||
Attempted to convert an absolute path not within home directory to a
|
||||
@@ -54,47 +61,47 @@ rec {
|
||||
'';
|
||||
|
||||
/*
|
||||
Convert a relative path to an absolute path by prepending the home directory.
|
||||
Convert a relative path to an absolute path.
|
||||
Returns RAW path (unescaped).
|
||||
|
||||
This function ensures paths are absolute by prepending the home directory
|
||||
to relative paths. Already absolute paths are returned unchanged (after cleaning).
|
||||
This function does NOT support shell variables.
|
||||
|
||||
Type: String -> String
|
||||
|
||||
Example:
|
||||
mkAbsPathStr "config" => "'/home/user/config'"
|
||||
mkAbsPathStr "/absolute/path" => "'/absolute/path'"
|
||||
mkAbsPathStr "config" => "/home/user/config"
|
||||
mkAbsPathStr "/absolute/path" => "/absolute/path"
|
||||
*/
|
||||
mkAbsPathStr =
|
||||
pathStr: cleanPathStr ((lib.optionalString (!lib.hasPrefix "/" pathStr) "${homeDir}/") + pathStr);
|
||||
pathStr:
|
||||
let
|
||||
normPath = stripSlash pathStr;
|
||||
in
|
||||
if lib.hasPrefix "/" normPath then normPath else "${homeDir}/${normPath}";
|
||||
|
||||
/*
|
||||
Convert a path to absolute form while preserving shell variables for runtime expansion.
|
||||
Convert a path to absolute form while preserving shell variables.
|
||||
Returns RAW path (unescaped) unless vars are present (then preserves vars).
|
||||
|
||||
This function handles both literal paths and shell variable expressions.
|
||||
Shell variables (containing '$') are preserved unescaped to allow runtime expansion.
|
||||
Literal paths are made absolute and properly escaped for shell usage.
|
||||
Literal paths are made absolute.
|
||||
|
||||
Type: String -> String
|
||||
|
||||
Example:
|
||||
mkShellVarPathStr "config" => "'/home/user/config'"
|
||||
mkShellVarPathStr "config" => "/home/user/config"
|
||||
mkShellVarPathStr "$HOME/config" => "$HOME/config"
|
||||
mkShellVarPathStr "\${XDG_CONFIG_HOME:-$HOME/.config}/app" => "\${XDG_CONFIG_HOME:-$HOME/.config}/app"
|
||||
*/
|
||||
mkShellVarPathStr =
|
||||
pathStr:
|
||||
let
|
||||
cleanPath = lib.removeSuffix "/" pathStr;
|
||||
hasShellVars = lib.hasInfix "$" cleanPath;
|
||||
normPath = stripSlash pathStr;
|
||||
hasShellVars = lib.hasInfix "$" normPath;
|
||||
in
|
||||
if hasShellVars then
|
||||
# Does not escape shell variables, allowing them to be expanded at runtime
|
||||
cleanPath
|
||||
else
|
||||
# For literal paths, make them absolute if needed and escape them
|
||||
cleanPathStr ((lib.optionalString (!lib.hasPrefix "/" cleanPath) "${homeDir}/") + cleanPath);
|
||||
if hasShellVars then normPath else mkAbsPathStr normPath;
|
||||
|
||||
dotDirAbs = mkAbsPathStr cfg.dotDir;
|
||||
dotDirRel = mkRelPathStr cfg.dotDir;
|
||||
@@ -107,5 +114,5 @@ rec {
|
||||
|
||||
Type: String
|
||||
*/
|
||||
pluginsDir = dotDirAbs + (lib.optionalString (homeDir == dotDirAbs) "/.zsh") + "/plugins";
|
||||
pluginsDir = dotDirAbs + (lib.optionalString (mkRelPathStr cfg.dotDir == ".") "/.zsh") + "/plugins";
|
||||
}
|
||||
|
||||
@@ -117,6 +117,8 @@ in
|
||||
systemd.user.services.easyeffects = {
|
||||
Unit = {
|
||||
Description = "Easyeffects daemon";
|
||||
After = [ "graphical-session.target" ];
|
||||
PartOf = [ "graphical-session.target" ];
|
||||
};
|
||||
|
||||
Install.WantedBy = [ "graphical-session.target" ];
|
||||
|
||||
@@ -84,13 +84,11 @@ in
|
||||
'';
|
||||
in
|
||||
{
|
||||
bash.initExtra = lib.mkIf cfg.enableBashIntegration bashIntegration;
|
||||
|
||||
zsh.initContent = lib.mkIf cfg.enableZshIntegration bashIntegration;
|
||||
|
||||
fish.interactiveShellInit = lib.mkIf cfg.enableFishIntegration fishIntegration;
|
||||
|
||||
nushell.extraConfig = lib.mkIf cfg.enableNushellIntegration nushellIntegration;
|
||||
# $SSH_AUTH_SOCK has to be set early since other tools rely on it
|
||||
bash.profileExtra = lib.mkIf cfg.enableBashIntegration (lib.mkOrder 900 bashIntegration);
|
||||
fish.shellInit = lib.mkIf cfg.enableFishIntegration (lib.mkOrder 900 fishIntegration);
|
||||
nushell.extraConfig = lib.mkIf cfg.enableNushellIntegration (lib.mkOrder 900 nushellIntegration);
|
||||
zsh.envExtra = lib.mkIf cfg.enableZshIntegration (lib.mkOrder 900 bashIntegration);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -274,6 +274,26 @@ in
|
||||
sed "s|${pkg.out}|$out|g" "$src" > "$dsk"
|
||||
done
|
||||
|
||||
# Patch systemd user services
|
||||
for svc in "$out/share/systemd/user"/*.service ; do
|
||||
if ! grep -q "${pkg.out}" "$svc"; then
|
||||
continue
|
||||
fi
|
||||
src="$(readlink "$svc")"
|
||||
rm "$svc"
|
||||
sed "s|${pkg.out}|$out|g" "$src" > "$svc"
|
||||
done
|
||||
|
||||
# Patch DBus services
|
||||
for svc in "$out/share/dbus-1/services"/*.service ; do
|
||||
if ! grep -q "${pkg.out}" "$svc"; then
|
||||
continue
|
||||
fi
|
||||
src="$(readlink "$svc")"
|
||||
rm "$svc"
|
||||
sed "s|${pkg.out}|$out|g" "$src" > "$svc"
|
||||
done
|
||||
|
||||
shopt -u nullglob # Revert nullglob back to its normal default state
|
||||
'';
|
||||
}))
|
||||
|
||||
@@ -130,6 +130,13 @@ let
|
||||
stateVersion = lib.mkDefault "18.09";
|
||||
};
|
||||
|
||||
# NOTE: Added 2025-12-27
|
||||
# Avoid option change deprecation warning
|
||||
# Remove after deprecation period
|
||||
programs.zsh.dotDir = lib.mkIf (config.home.stateVersion == "18.09") (
|
||||
lib.mkDefault "/home/hm-user"
|
||||
);
|
||||
|
||||
# Avoid including documentation since this will cause
|
||||
# unnecessary rebuilds of the tests.
|
||||
manual.manpages.enable = lib.mkDefault false;
|
||||
|
||||
@@ -2,4 +2,5 @@
|
||||
|
||||
lib.optionalAttrs pkgs.stdenv.hostPlatform.isLinux {
|
||||
anyrun = ./basic-config.nix;
|
||||
anyrun-empty-css = ./empty-css.nix;
|
||||
}
|
||||
|
||||
10
tests/modules/programs/anyrun/empty-css.nix
Normal file
10
tests/modules/programs/anyrun/empty-css.nix
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
programs.anyrun = {
|
||||
enable = true;
|
||||
config.plugins = [ ];
|
||||
};
|
||||
|
||||
nmt.script = ''
|
||||
assertPathNotExists home-files/.config/anyrun/style.css
|
||||
'';
|
||||
}
|
||||
@@ -5,6 +5,8 @@
|
||||
claude-code-assertion = ./assertion.nix;
|
||||
claude-code-memory-management = ./memory-management.nix;
|
||||
claude-code-memory-from-source = ./memory-from-source.nix;
|
||||
claude-code-rules-dir = ./rules-dir.nix;
|
||||
claude-code-rules-path = ./rules-path.nix;
|
||||
claude-code-agents-dir = ./agents-dir.nix;
|
||||
claude-code-commands-dir = ./commands-dir.nix;
|
||||
claude-code-hooks-dir = ./hooks-dir.nix;
|
||||
|
||||
14
tests/modules/programs/claude-code/rules-dir.nix
Normal file
14
tests/modules/programs/claude-code/rules-dir.nix
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
programs.claude-code = {
|
||||
enable = true;
|
||||
rulesDir = ./rules;
|
||||
};
|
||||
|
||||
nmt.script = ''
|
||||
assertFileExists home-files/.claude/rules/test-rule.md
|
||||
assertLinkExists home-files/.claude/rules/test-rule.md
|
||||
assertFileContent \
|
||||
home-files/.claude/rules/test-rule.md \
|
||||
${./rules/test-rule.md}
|
||||
'';
|
||||
}
|
||||
20
tests/modules/programs/claude-code/rules-path.nix
Normal file
20
tests/modules/programs/claude-code/rules-path.nix
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
programs.claude-code = {
|
||||
enable = true;
|
||||
rules = {
|
||||
test-rule = ./test-rule.md;
|
||||
inline-rule = ''
|
||||
# Inline Rule
|
||||
|
||||
This is an inline rule for testing.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
nmt.script = ''
|
||||
assertFileExists home-files/.claude/rules/test-rule.md
|
||||
assertFileContent home-files/.claude/rules/test-rule.md \
|
||||
${./test-rule.md}
|
||||
assertFileExists home-files/.claude/rules/inline-rule.md
|
||||
'';
|
||||
}
|
||||
9
tests/modules/programs/claude-code/rules/test-rule.md
Normal file
9
tests/modules/programs/claude-code/rules/test-rule.md
Normal file
@@ -0,0 +1,9 @@
|
||||
# Test Rule from Directory
|
||||
|
||||
This is a test rule loaded from a directory.
|
||||
Used to verify rulesDir support functionality.
|
||||
|
||||
## Best Practices
|
||||
|
||||
- Write clean code
|
||||
- Test thoroughly
|
||||
9
tests/modules/programs/claude-code/test-rule.md
Normal file
9
tests/modules/programs/claude-code/test-rule.md
Normal file
@@ -0,0 +1,9 @@
|
||||
# Test Rule
|
||||
|
||||
This is a test rule loaded from a file path.
|
||||
Used to verify path support functionality for rules.
|
||||
|
||||
## Guidelines
|
||||
|
||||
- Follow test conventions
|
||||
- Maintain code quality
|
||||
@@ -41,5 +41,9 @@
|
||||
assertFileExists home-files/.gemini/CONTEXT.md
|
||||
assertFileContent home-files/.gemini/CONTEXT.md \
|
||||
${./context-additional.md}
|
||||
|
||||
assertFileExists home-path/etc/profile.d/hm-session-vars.sh
|
||||
assertFileNotRegex home-path/etc/profile.d/hm-session-vars.sh \
|
||||
"GEMINI_MODEL"
|
||||
'';
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
programs.gemini-cli = {
|
||||
enable = true;
|
||||
defaultModel = "gemini-2.5-flash";
|
||||
settings = {
|
||||
theme = "Default";
|
||||
vimMode = true;
|
||||
@@ -28,5 +29,9 @@
|
||||
${./changelog.toml}
|
||||
assertFileContent home-files/.gemini/commands/git/fix.toml \
|
||||
${./fix.toml}
|
||||
|
||||
assertFileExists home-path/etc/profile.d/hm-session-vars.sh
|
||||
assertFileContains home-path/etc/profile.d/hm-session-vars.sh \
|
||||
'export GEMINI_MODEL="gemini-2.5-flash"'
|
||||
'';
|
||||
}
|
||||
|
||||
@@ -5,6 +5,9 @@ identity = "hm@example.com"
|
||||
root_mailbox = "/home/hm-user/Mail/hm@example.com"
|
||||
subscribed_mailboxes = ["Inbox", "Sent", "Trash", "Drafts"]
|
||||
|
||||
[accounts."hm@example.com".listing]
|
||||
index_style = "compact"
|
||||
|
||||
[accounts."hm@example.com".mailboxes]
|
||||
|
||||
[accounts."hm@example.com".send_mail]
|
||||
|
||||
@@ -16,7 +16,12 @@
|
||||
};
|
||||
accounts.email.accounts = {
|
||||
"hm@example.com" = {
|
||||
meli.enable = true;
|
||||
meli = {
|
||||
enable = true;
|
||||
settings = {
|
||||
listing.index_style = "compact";
|
||||
};
|
||||
};
|
||||
smtp.port = 1848;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
neovim-plugin-config = ./plugin-config.nix;
|
||||
neovim-coc-config = ./coc-config.nix;
|
||||
neovim-runtime = ./runtime.nix;
|
||||
neovim-wrapper-args = ./wrapper-args.nix;
|
||||
|
||||
# waiting for a nixpkgs patch
|
||||
neovim-no-init = ./no-init.nix;
|
||||
|
||||
@@ -34,15 +34,25 @@ lib.mkIf config.test.enableBig {
|
||||
|
||||
_module.args.pkgs = lib.mkForce realPkgs;
|
||||
|
||||
nmt.script = ''
|
||||
vimout=$(mktemp)
|
||||
echo "redir >> /dev/stdout | echo g:hmExtraConfig | echo g:hmPlugins | redir END" \
|
||||
| ${pkgs.neovim}/bin/nvim -es -u "$TESTED/home-files/.config/nvim/init.lua" \
|
||||
> "$vimout" || true
|
||||
assertFileContains "$vimout" "HM_EXTRA_CONFIG"
|
||||
assertFileContains "$vimout" "HM_PLUGINS_CONFIG"
|
||||
nmt.script =
|
||||
let
|
||||
# Force evaluation of generatedConfigs.
|
||||
luaConfig = config.programs.neovim.generatedConfigs.lua;
|
||||
vimlConfig = config.programs.neovim.generatedConfigs.viml;
|
||||
in
|
||||
''
|
||||
vimout=$(mktemp)
|
||||
echo "redir >> /dev/stdout | echo g:hmExtraConfig | echo g:hmPlugins | redir END" \
|
||||
| ${pkgs.neovim}/bin/nvim -es -u "$TESTED/home-files/.config/nvim/init.lua" \
|
||||
> "$vimout" || true
|
||||
assertFileContains "$vimout" "HM_EXTRA_CONFIG"
|
||||
assertFileContains "$vimout" "HM_PLUGINS_CONFIG"
|
||||
|
||||
initLua="$TESTED/home-files/.config/nvim/init.lua"
|
||||
assertFileContent $(normalizeStorePaths "$initLua") ${./plugin-config.expected}
|
||||
'';
|
||||
initLua="$TESTED/home-files/.config/nvim/init.lua"
|
||||
assertFileContent $(normalizeStorePaths "$initLua") ${./plugin-config.expected}
|
||||
|
||||
# Verify generatedConfigs evaluated properly (issue #8371)
|
||||
echo "Lua config length: ${toString (builtins.stringLength luaConfig)}"
|
||||
echo "Viml config length: ${toString (builtins.stringLength vimlConfig)}"
|
||||
'';
|
||||
}
|
||||
|
||||
83
tests/modules/programs/neovim/wrapper-args.nix
Normal file
83
tests/modules/programs/neovim/wrapper-args.nix
Normal file
@@ -0,0 +1,83 @@
|
||||
{
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
inherit (pkgs.stdenv.hostPlatform) isLinux;
|
||||
|
||||
dummyDep = pkgs.runCommand "dummy-dep" { } ''
|
||||
mkdir -p $out/bin
|
||||
echo "echo dummy" > $out/bin/dummy-dep-bin
|
||||
chmod +x $out/bin/dummy-dep-bin
|
||||
'';
|
||||
|
||||
dummyPlugin = pkgs.vimUtils.buildVimPlugin {
|
||||
pname = "dummy-plugin";
|
||||
version = "1.0";
|
||||
src = pkgs.writeTextDir "plugin/dummy.vim" "\" dummy";
|
||||
runtimeDeps = [ dummyDep ];
|
||||
};
|
||||
in
|
||||
{
|
||||
imports = [ ./stubs.nix ];
|
||||
tests.stubs.wl-clipboard = { };
|
||||
|
||||
programs.neovim = {
|
||||
enable = true;
|
||||
extraName = "-my-suffix";
|
||||
withPerl = true;
|
||||
withPython3 = true;
|
||||
withRuby = true;
|
||||
withNodeJs = true;
|
||||
autowrapRuntimeDeps = true;
|
||||
waylandSupport = isLinux;
|
||||
plugins = [ dummyPlugin ];
|
||||
};
|
||||
|
||||
nmt.script = ''
|
||||
nvimBin="home-path/bin/nvim"
|
||||
|
||||
assertBinaryContains() {
|
||||
local file="$TESTED/$1"
|
||||
if [[ $1 == /* ]]; then file="$1"; fi
|
||||
|
||||
if ! grep -a -qF -- "$2" "$file"; then
|
||||
fail "Expected binary file '$1' to contain '$2' but it did not."
|
||||
fi
|
||||
}
|
||||
|
||||
# Ensure the main binary exists
|
||||
assertFileExists "$nvimBin"
|
||||
|
||||
# 1. extraName: Check if the suffix is in the rplugin manifest path within the wrapper
|
||||
assertBinaryContains "$nvimBin" "-my-suffix/rplugin.vim"
|
||||
|
||||
# 2. withPerl: Check if nvim-perl binary exists and host prog is set
|
||||
assertFileExists "home-path/bin/nvim-perl"
|
||||
assertBinaryContains "$nvimBin" "perl_host_prog="
|
||||
|
||||
# 3. withPython3: Check if nvim-python3 binary exists and host prog is set
|
||||
assertFileExists "home-path/bin/nvim-python3"
|
||||
assertBinaryContains "$nvimBin" "python3_host_prog="
|
||||
|
||||
# 4. withRuby: Check if nvim-ruby binary exists, GEM_HOME and host prog are set
|
||||
assertFileExists "home-path/bin/nvim-ruby"
|
||||
assertBinaryContains "$nvimBin" "GEM_HOME="
|
||||
assertBinaryContains "$nvimBin" "ruby_host_prog="
|
||||
|
||||
# 5. withNodeJs: Check if nvim-node binary exists and host prog is set
|
||||
assertFileExists "home-path/bin/nvim-node"
|
||||
assertBinaryContains "$nvimBin" "node_host_prog="
|
||||
|
||||
# 6. waylandSupport: Check for wl-clipboard path in wrapper's PATH modification
|
||||
# We check for the store path of wl-clipboard in the current pkgs
|
||||
${lib.optionalString isLinux ''
|
||||
assertBinaryContains "$nvimBin" "wl-clipboard-"
|
||||
''}
|
||||
|
||||
# 7. autowrapRuntimeDeps: Check for dummyDep path in wrapper's PATH modification
|
||||
assertBinaryContains "$nvimBin" "${dummyDep}/bin"
|
||||
'';
|
||||
}
|
||||
@@ -1,9 +1,19 @@
|
||||
{ config, pkgs, ... }:
|
||||
|
||||
{
|
||||
config = {
|
||||
programs.radicle.enable = true;
|
||||
|
||||
test.stubs.radicle-node = {
|
||||
buildScript = ''
|
||||
mkdir -p "$out/bin"
|
||||
cat > "$out/bin/rad" << 'EOF'
|
||||
#!/bin/sh
|
||||
# Stub rad command that does nothing
|
||||
exit 0
|
||||
EOF
|
||||
chmod +x "$out/bin/rad"
|
||||
'';
|
||||
};
|
||||
|
||||
nmt.script = ''
|
||||
assertFileContent \
|
||||
home-files/.radicle/config.json \
|
||||
|
||||
@@ -15,6 +15,7 @@ Host xyz
|
||||
RemoteForward [localhost]:8081 [10.0.0.2]:80
|
||||
RemoteForward /run/user/1000/gnupg/S.gpg-agent.extra /run/user/1000/gnupg/S.gpg-agent
|
||||
DynamicForward [localhost]:2839
|
||||
KexAlgorithms sntrup761x25519-sha512,sntrup761x25519-sha512@openssh.com,mlkem768x25519-sha256
|
||||
|
||||
Host ordered
|
||||
Port 1
|
||||
|
||||
@@ -34,6 +34,11 @@
|
||||
host.address = "/run/user/1000/gnupg/S.gpg-agent";
|
||||
}
|
||||
];
|
||||
kexAlgorithms = [
|
||||
"sntrup761x25519-sha512"
|
||||
"sntrup761x25519-sha512@openssh.com"
|
||||
"mlkem768x25519-sha256"
|
||||
];
|
||||
dynamicForwards = [ { port = 2839; } ];
|
||||
setEnv = {
|
||||
FOO = "foo12";
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{ lib, pkgs, ... }:
|
||||
lib.optionalAttrs (pkgs.stdenv.hostPlatform.isLinux) {
|
||||
vicinae-pre17-settings = ./pre17-settings.nix;
|
||||
vicinae-example-settings = ./example-settings.nix;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
programs.vicinae = {
|
||||
enable = true;
|
||||
systemd.enable = true;
|
||||
|
||||
useLayerShell = false;
|
||||
settings = {
|
||||
faviconService = "twenty";
|
||||
font = {
|
||||
@@ -80,11 +80,16 @@
|
||||
];
|
||||
};
|
||||
|
||||
test.asserts.assertions.expected = [
|
||||
''After version 0.17, if you want to explicitly disable the use of layer shell, you need to set {option}.programs.vicinae.settings.launcher_window.layer_shell.enabled = false.''
|
||||
];
|
||||
|
||||
nmt.script = ''
|
||||
assertFileExists "home-files/.config/vicinae/vicinae.json"
|
||||
assertFileExists "home-files/.config/vicinae/settings.json"
|
||||
assertFileExists "home-files/.config/systemd/user/vicinae.service"
|
||||
assertFileExists "home-files/.local/share/vicinae/themes/catppuccin-mocha.toml"
|
||||
assertFileExists "home-files/.local/share/vicinae/extensions/gif-search/package.json"
|
||||
assertFileExists "home-files/.local/share/vicinae/extensions/test-extension/package.json"
|
||||
assertFileContent "home-files/.config/systemd/user/vicinae.service" ${./service.service}
|
||||
'';
|
||||
}
|
||||
|
||||
46
tests/modules/programs/vicinae/pre17-settings.nix
Normal file
46
tests/modules/programs/vicinae/pre17-settings.nix
Normal file
@@ -0,0 +1,46 @@
|
||||
{
|
||||
pkgs,
|
||||
config,
|
||||
...
|
||||
}:
|
||||
|
||||
{
|
||||
programs.vicinae = {
|
||||
enable = true;
|
||||
systemd.enable = true;
|
||||
package = pkgs.stdenv.mkDerivation {
|
||||
pname = "fake-vicinae";
|
||||
version = "0.10.0";
|
||||
src = pkgs.emptyFile;
|
||||
buildCommand = "mkdir -p $out";
|
||||
meta = {
|
||||
mainProgram = "vicinae";
|
||||
};
|
||||
};
|
||||
|
||||
settings = {
|
||||
faviconService = "twenty";
|
||||
font = {
|
||||
size = 10;
|
||||
};
|
||||
popToRootOnClose = false;
|
||||
rootSearch = {
|
||||
searchFiles = false;
|
||||
};
|
||||
theme = {
|
||||
name = "vicinae-dark";
|
||||
};
|
||||
window = {
|
||||
csd = true;
|
||||
opacity = 0.95;
|
||||
rounding = 10;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
nmt.script = ''
|
||||
assertFileExists "home-files/.config/vicinae/vicinae.json"
|
||||
assertFileExists "home-files/.config/systemd/user/vicinae.service"
|
||||
assertFileContains "home-files/.config/systemd/user/vicinae.service" "EnvironmentFile"
|
||||
'';
|
||||
}
|
||||
15
tests/modules/programs/vicinae/service.service
Normal file
15
tests/modules/programs/vicinae/service.service
Normal file
@@ -0,0 +1,15 @@
|
||||
[Install]
|
||||
WantedBy=graphical-session.target
|
||||
|
||||
[Service]
|
||||
ExecStart=@vicinae@/bin/vicinae server
|
||||
KillMode=process
|
||||
Restart=always
|
||||
RestartSec=5
|
||||
Type=simple
|
||||
|
||||
[Unit]
|
||||
After=graphical-session.target
|
||||
Description=Vicinae server daemon
|
||||
Documentation=https://docs.vicinae.com
|
||||
PartOf=graphical-session.target
|
||||
@@ -3,6 +3,13 @@
|
||||
zsh-aliases = ./aliases.nix;
|
||||
zsh-dotdir-absolute = import ./dotdir.nix "absolute";
|
||||
zsh-dotdir-default = import ./dotdir.nix "default";
|
||||
zsh-dotdir-path-normalization-abs-no-slash = import ./dotdir.nix "abs-no-slash";
|
||||
zsh-dotdir-path-normalization-abs-slash = import ./dotdir.nix "abs-slash";
|
||||
zsh-dotdir-path-normalization-rel-no-slash = import ./dotdir.nix "rel-no-slash";
|
||||
zsh-dotdir-path-normalization-rel-slash = import ./dotdir.nix "rel-slash";
|
||||
zsh-dotdir-path-normalization-root-no-slash = import ./dotdir.nix "root-no-slash";
|
||||
zsh-dotdir-path-normalization-root-slash = import ./dotdir.nix "root-slash";
|
||||
zsh-dotdir-path-normalization-abs-space = import ./dotdir.nix "abs-space";
|
||||
zsh-dotdir-relative = import ./dotdir.nix "relative";
|
||||
zsh-dotdir-shell-variable = import ./dotdir.nix "shell-variable";
|
||||
zsh-history-ignore-pattern = ./history-ignore-pattern.nix;
|
||||
@@ -12,12 +19,15 @@
|
||||
zsh-history-path-xdg-variable = import ./history-path.nix "xdg-variable";
|
||||
zsh-history-path-zdotdir-variable = import ./history-path.nix "zdotdir-variable";
|
||||
zsh-history-substring-search = ./history-substring-search.nix;
|
||||
zsh-legacy-warning = ./legacy-warning.nix;
|
||||
zsh-siteFunctions-mkcd = ./siteFunctions-mkcd.nix;
|
||||
zsh-plugins = ./plugins.nix;
|
||||
zsh-prezto = ./prezto.nix;
|
||||
zsh-session-variables = ./session-variables.nix;
|
||||
zsh-smart-formatting = ./smart-formatting.nix;
|
||||
zsh-syntax-highlighting = ./syntax-highlighting.nix;
|
||||
zsh-xdg-default = ./xdg-default.nix;
|
||||
zsh-xdg-disabled = ./xdg-disabled.nix;
|
||||
zsh-zprof = ./zprof.nix;
|
||||
zshrc-contents-priorities = ./zshrc-content-priorities.nix;
|
||||
}
|
||||
|
||||
@@ -7,24 +7,46 @@ case:
|
||||
}:
|
||||
let
|
||||
home = config.home.homeDirectory;
|
||||
subDir = "subdir/subdir2";
|
||||
|
||||
dotDir =
|
||||
dotDirCases = {
|
||||
absolute = "${home}/${subDir}";
|
||||
relative = subDir;
|
||||
inherit (options.programs.zsh.dotDir) default;
|
||||
shell-variable = "\${XDG_CONFIG_HOME:-$HOME/.config}/zsh";
|
||||
|
||||
# Path normalization cases
|
||||
abs-no-slash = "${home}/subdir";
|
||||
abs-slash = "${home}/subdir/";
|
||||
rel-no-slash = "subdir";
|
||||
rel-slash = "subdir/";
|
||||
root-no-slash = "${home}";
|
||||
root-slash = "${home}/";
|
||||
abs-space = "${home}/subdir with space";
|
||||
};
|
||||
|
||||
dotDir = dotDirCases.${case} or (abort "Unknown case: ${case}");
|
||||
|
||||
# Normalize absolute path to match module behavior (no trailing slash)
|
||||
absDotDir =
|
||||
let
|
||||
subDir = "subdir/subdir2";
|
||||
fullPath = if lib.hasPrefix "/" dotDir then dotDir else "${home}/${dotDir}";
|
||||
in
|
||||
if case == "absolute" then
|
||||
"${home}/${subDir}"
|
||||
else if case == "relative" then
|
||||
subDir
|
||||
else if case == "default" then
|
||||
options.programs.zsh.dotDir.default
|
||||
else if case == "shell-variable" then
|
||||
"\${XDG_CONFIG_HOME:-\$HOME/.config}/zsh"
|
||||
else
|
||||
abort "Test condition not provided.";
|
||||
lib.removeSuffix "/" fullPath;
|
||||
|
||||
absDotDir = lib.optionalString (!lib.hasPrefix home dotDir) "${home}/" + dotDir;
|
||||
relDotDir = lib.removePrefix home dotDir;
|
||||
# Calculate relative path for file location assertions
|
||||
relDotDir =
|
||||
let
|
||||
# Use the normalized absDotDir to determine relative location
|
||||
rawRel = lib.removePrefix home absDotDir;
|
||||
in
|
||||
if lib.hasPrefix "/" rawRel then lib.removePrefix "/" rawRel else rawRel;
|
||||
|
||||
isRelative = lib.elem case [
|
||||
"relative"
|
||||
"rel-no-slash"
|
||||
"rel-slash"
|
||||
];
|
||||
in
|
||||
{
|
||||
config = {
|
||||
@@ -33,33 +55,37 @@ in
|
||||
inherit dotDir;
|
||||
};
|
||||
|
||||
test.stubs.zsh = { };
|
||||
test = {
|
||||
stubs.zsh = { };
|
||||
|
||||
test.asserts.warnings.expected = lib.optionals (case == "relative") [
|
||||
''
|
||||
Using relative paths in programs.zsh.dotDir is deprecated and will be removed in a future release.
|
||||
Current dotDir: subdir/subdir2
|
||||
Consider using absolute paths or home-manager config options instead.
|
||||
You can replace relative paths or environment variables with options like:
|
||||
- config.home.homeDirectory (user's home directory)
|
||||
- config.xdg.configHome (XDG config directory)
|
||||
- config.xdg.dataHome (XDG data directory)
|
||||
- config.xdg.cacheHome (XDG cache directory)
|
||||
''
|
||||
];
|
||||
asserts = {
|
||||
assertions.expected = lib.optionals (case == "shell-variable") [
|
||||
''
|
||||
programs.zsh.dotDir cannot contain shell variables as it is used for file creation at build time.
|
||||
Current dotDir: ''${XDG_CONFIG_HOME:-''$HOME/.config}/zsh
|
||||
Consider using an absolute path or home-manager config options instead.
|
||||
You can replace shell variables with options like:
|
||||
- config.home.homeDirectory (user's home directory)
|
||||
- config.xdg.configHome (XDG config directory)
|
||||
- config.xdg.dataHome (XDG data directory)
|
||||
- config.xdg.cacheHome (XDG cache directory)
|
||||
''
|
||||
];
|
||||
|
||||
test.asserts.assertions.expected = lib.optionals (case == "shell-variable") [
|
||||
''
|
||||
programs.zsh.dotDir cannot contain shell variables as it is used for file creation at build time.
|
||||
Current dotDir: ''${XDG_CONFIG_HOME:-''$HOME/.config}/zsh
|
||||
Consider using an absolute path or home-manager config options instead.
|
||||
You can replace shell variables with options like:
|
||||
- config.home.homeDirectory (user's home directory)
|
||||
- config.xdg.configHome (XDG config directory)
|
||||
- config.xdg.dataHome (XDG data directory)
|
||||
- config.xdg.cacheHome (XDG cache directory)
|
||||
''
|
||||
];
|
||||
warnings.expected = lib.optionals isRelative [
|
||||
''
|
||||
Using relative paths in programs.zsh.dotDir is deprecated and will be removed in a future release.
|
||||
Current dotDir: ${dotDir}
|
||||
Consider using absolute paths or home-manager config options instead.
|
||||
You can replace relative paths or environment variables with options like:
|
||||
- config.home.homeDirectory (user's home directory)
|
||||
- config.xdg.configHome (XDG config directory)
|
||||
- config.xdg.dataHome (XDG data directory)
|
||||
- config.xdg.cacheHome (XDG cache directory)
|
||||
''
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
nmt.script =
|
||||
if case == "shell-variable" then
|
||||
@@ -70,17 +96,15 @@ in
|
||||
else
|
||||
lib.concatStringsSep "\n" [
|
||||
# check dotDir entrypoint exists
|
||||
"assertFileExists home-files/${relDotDir}/.zshenv"
|
||||
"assertFileExists 'home-files/${if relDotDir == "" then "" else "${relDotDir}/"}.zshenv'"
|
||||
|
||||
# for non-default dotDir only:
|
||||
(lib.optionalString (case != "default") ''
|
||||
(lib.optionalString (absDotDir != home) ''
|
||||
# check .zshenv in homeDirectory sources .zshenv in dotDir
|
||||
assertFileRegex home-files/.zshenv \
|
||||
"source [\"']\?${absDotDir}/.zshenv[\"']\?"
|
||||
assertFileRegex home-files/.zshenv "source ${lib.escapeShellArg "${absDotDir}/.zshenv"}"
|
||||
|
||||
# check that .zshenv in dotDir exports ZDOTDIR
|
||||
assertFileRegex home-files/${relDotDir}/.zshenv \
|
||||
"export ZDOTDIR=[\"']\?${absDotDir}[\"']\?"
|
||||
assertFileRegex 'home-files/${relDotDir}/.zshenv' "export ZDOTDIR=\"${absDotDir}\""
|
||||
'')
|
||||
];
|
||||
};
|
||||
|
||||
@@ -68,7 +68,7 @@ in
|
||||
else if case == "zdotdir-variable" then
|
||||
''
|
||||
assertFileContains home-files/.config/zsh/.zshrc 'HISTFILE="$ZDOTDIR/.zsh_history"'
|
||||
assertFileContains home-files/.config/zsh/.zshenv "export ZDOTDIR=${homeDir}/.config/zsh"
|
||||
assertFileContains home-files/.config/zsh/.zshenv "export ZDOTDIR=\"${homeDir}/.config/zsh\""
|
||||
''
|
||||
else
|
||||
''
|
||||
|
||||
29
tests/modules/programs/zsh/legacy-warning.nix
Normal file
29
tests/modules/programs/zsh/legacy-warning.nix
Normal file
@@ -0,0 +1,29 @@
|
||||
{
|
||||
config = {
|
||||
programs.zsh.enable = true;
|
||||
xdg.enable = true;
|
||||
|
||||
# We use 25.05 to trigger the legacy warning (since < 26.05)
|
||||
# AND to bypass the global fix in tests/default.nix (which checks for 18.09)
|
||||
home.stateVersion = "25.05";
|
||||
|
||||
nmt.script = ''
|
||||
assertFileExists home-files/.zshrc
|
||||
|
||||
# Verify that the warning is generated
|
||||
# We check the evaluation output for the warning message
|
||||
assertPathNotExists home-files/.config/zsh
|
||||
'';
|
||||
|
||||
test.asserts.warnings.expected = [
|
||||
''
|
||||
The default value of `programs.zsh.dotDir` will change in future versions.
|
||||
You are currently using the legacy default (home directory) because `home.stateVersion` is less than "26.05".
|
||||
To silence this warning and lock in the current behavior, set:
|
||||
programs.zsh.dotDir = config.home.homeDirectory;
|
||||
To adopt the new behavior (XDG config directory), set:
|
||||
programs.zsh.dotDir = "''${config.xdg.configHome}/zsh";
|
||||
''
|
||||
];
|
||||
};
|
||||
}
|
||||
21
tests/modules/programs/zsh/xdg-default.nix
Normal file
21
tests/modules/programs/zsh/xdg-default.nix
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
config = {
|
||||
programs.zsh.enable = true;
|
||||
home.stateVersion = "26.05";
|
||||
xdg.enable = true;
|
||||
|
||||
# With xdg.enable = true and new state version, dotDir should default to XDG config home
|
||||
|
||||
nmt.script = ''
|
||||
assertFileExists home-files/.config/zsh/.zshenv
|
||||
assertFileExists home-files/.config/zsh/.zshrc
|
||||
|
||||
# Verify global .zshenv points to the XDG location
|
||||
assertFileExists home-files/.zshenv
|
||||
assertFileRegex home-files/.zshenv "source /home/hm-user/.config/zsh/.zshenv"
|
||||
|
||||
# Verify ZDOTDIR is exported in the inner .zshenv
|
||||
assertFileRegex home-files/.config/zsh/.zshenv "export ZDOTDIR=\"/home/hm-user/.config/zsh\""
|
||||
'';
|
||||
};
|
||||
}
|
||||
20
tests/modules/programs/zsh/xdg-disabled.nix
Normal file
20
tests/modules/programs/zsh/xdg-disabled.nix
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
config = {
|
||||
programs.zsh.enable = true;
|
||||
xdg.enable = false;
|
||||
home.stateVersion = "26.05";
|
||||
|
||||
# With xdg.enable = false, dotDir should default to home directory regardless of state version
|
||||
|
||||
nmt.script = ''
|
||||
assertFileExists home-files/.zshenv
|
||||
assertFileExists home-files/.zshrc
|
||||
|
||||
# Should NOT exist in XDG location
|
||||
assertPathNotExists home-files/.config/zsh
|
||||
|
||||
# Verify ZDOTDIR is NOT exported (or points to home if it is, but usually it isn't if dotDir is home)
|
||||
assertFileNotRegex home-files/.zshenv "export ZDOTDIR="
|
||||
'';
|
||||
};
|
||||
}
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
nmt.script = ''
|
||||
assertFileContains \
|
||||
home-files/.bashrc \
|
||||
home-files/.profile \
|
||||
'export SSH_AUTH_SOCK=$(@getconf-system_cmds@/bin/getconf DARWIN_USER_TEMP_DIR)/ssh-agent'
|
||||
'';
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user