VS Code and Nix IDE - newbie problems

I’m sure it will be obvious shortly: I’m not a dev. I started using NixOS last week and love building my configuration files but working without an IDE was driving me bonkers. I’ve previously done some amateur coding using VS Code so I want to keep using that as an IDE.

However, I really struggled to understand how to set up the VS Code environment so it actually worked.

Obviously, the VS Code extensions Nix Environment and Nix IDE were required. No problems there. However, I just got a bunch of errors after using them. Typically, the errors related to “cannot find language server” and “cannot find nixpkgs-fmt”. Unfortunately, the instructions didn’t provide enough details for me to figure out what to actually do. In particular, Nix IDE has a bunch of instructions under “Language Servers” and “Formatting”, but no explanation as to where you are supposed to put this code. I guess all the pros just know.

I think I’ve solved the problems through these two steps:

Step One

nixd1 and nixpkgs-fmt are NixOS packages which need to be added to your NixOS user environment. You can do tihs through sudo nix-shell -p nixd && sudo nix-shell -p nixpkgs-fmt, or add them to your configuration.nix file. I added them to vscode.nix which is imported by configuration.nix. The contents of vscode.nix are:

# vscode.nix
{ pkgs, ... }: {
  environment.systemPackages = with pkgs; [
    vscode # Microsoft VS Code
    nixpkgs-fmt # nix code formatter for nixpkgs
    nixd # nix Language Server Protocal (LSP)
    direnv # 
  ];

  environment.sessionVariables.NIXOS_OZONE_WL = "1"; # required per nixos wiki page on VS Code
}

Step Two

The rest of the code is added to settings.json in VS Code. Use Ctrl + Shift + P and search for the file, then add the code. My settings.json file now includes:

    // Nix settings
    "nix.formatterPath": [
        "nixpkgs-fmt",
        "nixfmt",
        "treefmt",
        "--stdin",
        "{file}",
        "nix",
        "fmt",
        "--",
        "-",
    ],
    "nix.enableLanguageServer": true,
    "nix.serverPath": "nixd",
    "nix.serverSettings": {
        "nixd": {
            "formatting": {
                "command": [
                    "nixpkgs-fmt"
                ]
            },
            "options": {
                // By default, this entriy will be read from `import <nixpkgs> { }`.
                // You can write arbitary Nix expressions here, to produce valid "options" declaration result.
                // Tip: for flake-based configuration, utilize `builtins.getFlake`
                "nixos": {
                    "expr": "(builtins.getFlake \"/absolute/path/to/flake\").nixosConfigurations.<name>.options"
                },
                "home-manager": {
                    "expr": "(builtins.getFlake \"/absolute/path/to/flake\").homeConfigurations.<name>.options"
                },
                // Tip: use ${workspaceFolder} variable to define path
                "nix-darwin": {
                    "expr": "(builtins.getFlake \"${workspaceFolder}/path/to/flake\").darwinConfigurations.<name>.options"
                }
            }
        }
    },

With those steps done, my .nix files are now being formatted, unused code is highlighted, obvious errors relating to syntax seem to be highlighted, and I’m getting some information relating to packages.

Just me?

It seems there is also support for options. I.e. they are presented as I’m typing, such as this:

However, if I add something that seems bogus, there are no error messages. For example, this code is nonsense, as the option requires a list of packages.

environment.gnome.excludePackages = 100;

And this just has a spelling error (missing the “k” in “kernel”):
boot.ernelModules = [ "kvm-intel" ];

However, no error is given for either by the IDE. Using nixos-rebuild catches the error in compile time, but I would have thought the IDE would catch it earlier.

Unresolved errors

I’m not sure if these are bugs in the IDE, or something I’m missing, but if I create an empty file like empty.nix and focus on it, the IDE spits out these errors:

I[16:08:50.948] 3992: → reply:textDocument/documentSymbol(272) AST is null on this unit, error: 3
[Error - 16:08:50] Request textDocument/documentSymbol failed.
Message: AST is null on this unit
Code: -32001
[Error - 16:08:50] Request textDocument/documentSymbol failed.
Message: AST is null on this unit
Code: -32001
I[16:08:51.599] 3992: ← textDocument/documentLink(273)
I[16:08:51.599] 3992: → reply:textDocument/documentLink(273) AST is null on this unit, error: 3
[Error - 16:08:51] Request textDocument/documentLink failed.
Message: AST is null on this unit
Code: -32001
I[16:08:51.849] 3992: ← textDocument/inlayHint(274)
I[16:08:51.849] 3992: → reply:textDocument/inlayHint(274) AST is null on this unit, error: 3
[Error - 16:08:51] Request textDocument/inlayHint failed.
Message: AST is null on this unit
Code: -32001

As soon as a single character is added to the file, the errors stop, so I suspect a bug.

Questions for more experienced users

  1. Did I get Step Two correct?
  2. Am I missing a setting for the IDE which would prevent bad inputs such as those listed above?
  3. Are the Code -32001 errors a bug, or another missed setting?

Thanks, non-noobs!