oxalica / nil

NIx Language server, an incremental analysis assistant for writing in Nix.
Apache License 2.0
1.28k stars 39 forks source link

Option-completion with `mk*`-functions in NixOS modules? #104

Open Ma27 opened 1 year ago

Ma27 commented 1 year ago

While the trivial case of

{ config, lib, ... }:

with lib;

let cfg = config.foo.bar; in
{
  config = {
    # completion here, yay!
  };
}

works fine, but the case that's mostly used for modules, i.e. config = mkIf cfg.enable doesn't work.

I assume that this is because https://github.com/oxalica/nil/blob/97abe7d3d48721d4e0fcc1876eea83bb4247825b/crates/ide/src/ty/known.rs#L416 expects an attribute-set with options and not a function-call with an attr-set as argument.

One trivial hack would be to perform completion of options (or attr-sets defined in ty::known) on the attr-set that is an argument to the lambda invocation on config (i.e. to just complete options by convention inside the second argument to mkIf in the example above). This would probably cover ~95% of all cases, but is rather dirty (which is one of the reasons why I'm opening an issue to discuss this first)[1].

However, this doesn't account for more sophisticated examples such as config = mkMerge [ ... ]; or

{ lib, ... }: {
  config = {
    systemd = mkMerge [
      (mkIf true {
        # completion here?
      })
    ];
  };
}

I'm not sure if it's even possible to get that far without implementing even more evaluation logic (AFAIU right now we only evaluate Nix code to gather information about NixOS options), so I'm not sure if that's desirable in this project (second reason for the issue).

Finally, I'd like to say thank you very much for this beautiful tool, it's a huge improvement when working on Nix code these days!

[1] It'd also be possible to just check for the next attr-set in the AST to cover cases like apps = nixpkgs.lib.genAttrs systems (system: { myapp = { /* completion here */ }; }).

oxalica commented 12 months ago

If we can make mk* typed, it's possible to infer argument types through the result type. Maybe we can inject some common function types inside the lib parameter, but that's tedious and needs regular maintenance. And of course, we do not implemented polymorphic types yet.

One trivial hack would be to perform completion of options (or attr-sets defined in ty::known) on the attr-set that is an argument to the lambda invocation on config (i.e. to just complete options by convention inside the second argument to mkIf in the example above).

I'm afraid if it would produce too many false-positives. Attrsets are quite common for various usages, not always as configs.

DrymarchonShaun commented 11 months ago

EDIT: going to make a separate issue, I don't think this is what I'm experiencing.