about summary refs log tree commit diff
path: root/lib/tests/modules
diff options
context:
space:
mode:
authorJade Lovelace <software@lfcode.ca>2022-12-28 23:39:05 +0100
committerJade Lovelace <software@lfcode.ca>2023-09-08 11:48:40 +0200
commita1d38823079bdf7836dd44392e5e1029087d8c85 (patch)
tree8aaea0a9751c780533df6c784565864196bb89ee /lib/tests/modules
parent450d6437aeda4c90bb948f6f1f7d25a005ab9bb2 (diff)
downloadnixlib-a1d38823079bdf7836dd44392e5e1029087d8c85.tar
nixlib-a1d38823079bdf7836dd44392e5e1029087d8c85.tar.gz
nixlib-a1d38823079bdf7836dd44392e5e1029087d8c85.tar.bz2
nixlib-a1d38823079bdf7836dd44392e5e1029087d8c85.tar.lz
nixlib-a1d38823079bdf7836dd44392e5e1029087d8c85.tar.xz
nixlib-a1d38823079bdf7836dd44392e5e1029087d8c85.tar.zst
nixlib-a1d38823079bdf7836dd44392e5e1029087d8c85.zip
nixos/modules: Add declarationPositions
What it does: line and column level *declaration* position information:

$ nix repl .
nix-repl> :p nixosConfigurations.micro.options.environment.systemPackages.declarationPositions
[ { column = 7; file = "/nix/store/24aj3k7fgqv3ly7qkbf98qvphasrw9nb-source/nixos/modules/config/system-path.nix"; line = 63; } ]

Use cases:
- ctags over NixOS options, as will be presented at NixCon 2023 ;)
- improving the documentation pages to go to the exact line of the
  declarations.

Related work:
- https://github.com/NixOS/nixpkgs/pull/65024

  This one does it for all *definitions* rather than declarations, and
  it was not followed through with due to performance worries.
- https://github.com/NixOS/nixpkgs/pull/208173

  The basis for this change. This change is just a rebase of that one.
  I split it out to add the capability before adding users of it, in
  order to simplify review. However, the ctags script in there is a
  sample user of this feature.

Benchmarks: conducted by evaluating my own reasonably complex NixOS
configuration with the command:
`hyperfine -S none -w 1 -- "nix eval .#nixosConfigurations.snowflake.config.system.build.toplevel.outPath"`

```
Benchmark 1: nix eval .#nixosConfigurations.snowflake.config.system.build.toplevel.outPath
  Time (mean ± σ):      8.971 s ±  0.254 s    [User: 5.872 s, System: 1.388 s]
  Range (min … max):    8.574 s …  9.327 s    10 runs

Benchmark 1: nix eval .#nixosConfigurations.snowflake.config.system.build.toplevel.outPath
  Time (mean ± σ):      8.766 s ±  0.160 s    [User: 5.873 s, System: 1.346 s]
  Range (min … max):    8.496 s …  9.033 s    10 runs
```

Summary of results: it seems to be in the noise, this does not cause any
visible regression in times.
Diffstat (limited to 'lib/tests/modules')
-rw-r--r--lib/tests/modules/declaration-positions.nix49
1 files changed, 49 insertions, 0 deletions
diff --git a/lib/tests/modules/declaration-positions.nix b/lib/tests/modules/declaration-positions.nix
new file mode 100644
index 000000000000..cefd3b4e516f
--- /dev/null
+++ b/lib/tests/modules/declaration-positions.nix
@@ -0,0 +1,49 @@
+{ lib, options, ... }:
+let discardPositions = lib.mapAttrs (k: v: v);
+in
+# unsafeGetAttrPos is unspecified best-effort behavior, so we only want to consider this test on an evaluator that satisfies some basic assumptions about this function.
+assert builtins.unsafeGetAttrPos "a" { a = true; } != null;
+assert builtins.unsafeGetAttrPos "a" (discardPositions { a = true; }) == null;
+{
+  imports = [
+    {
+      options.imported.line10 = lib.mkOption {
+        type = lib.types.int;
+      };
+
+      # Simulates various patterns of generating modules such as
+      # programs.firefox.nativeMessagingHosts.ff2mpv. We don't expect to get
+      # line numbers for these, but we can fall back on knowing the file.
+      options.generated = discardPositions {
+        line18 = lib.mkOption {
+          type = lib.types.int;
+        };
+      };
+
+      options.submoduleLine34.extraOptLine23 = lib.mkOption {
+        default = 1;
+        type = lib.types.int;
+      };
+    }
+  ];
+
+  options.nested.nestedLine30 = lib.mkOption {
+    type = lib.types.int;
+  };
+
+  options.submoduleLine34 = lib.mkOption {
+    default = { };
+    type = lib.types.submoduleWith {
+      modules = [
+        ({ options, ... }: {
+          options.submodDeclLine39 = lib.mkOption { };
+        })
+        { freeformType = with lib.types; lazyAttrsOf (uniq unspecified); }
+      ];
+    };
+  };
+
+  config = {
+    submoduleLine34.submodDeclLine39 = (options.submoduleLine34.type.getSubOptions [ ]).submodDeclLine39.declarationPositions;
+  };
+}