VSCode extensions setup

As far as I can tell, one cannot easily use cpptools in visual studio code, due the C# executable. Thus, developing C/C++ is not really possible with VSCode on NixOS.

To work around this, we can use vscode-with-extension and use an overlay to install cpptools via nix:

self: super:
{
    mycode = super.vscode-with-extensions.override {
        # When the extension is already available in the default extensions set.
        vscodeExtensions = with super.vscode-extensions; [
            ms-vscode.cpptools
        ];
    };
}

However, this removes all existing plugins, from VSCode and prevents you from installing them via VSCode Marketplace.
We can also work around this, by declaring every extension in our above setup.
This is where the problem arises, in my opinion, for every unpackaged extension, I have to declare it, include the commit and if I want to update I need to modify the commit hash.
It seems unfeasible for me to update manually the commit hash of each package that I want to update.

Are my observations correct?
Could I automate the updating of plugins?
Can I install cpptools without declaring it explicitly in a vscode-with-extensions?

2 Likes

Good you’ve mentioned you are using NixOS (because my method won’t work with plain Nix)

My recipe for this is to install extensions imperatively. In those rare cases when it is not possible (cpptools requires some native libs, lucky it is packaged in Nixpkgs), use this module:

vscode.nix

{ config, pkgs, lib, ... }: {
    options = {
        vscode.extensions = lib.mkOption { default = []; };
        vscode.user = lib.mkOption { };     # <- Must be supplied
        vscode.homeDir = lib.mkOption { };  # <- Must be supplied
        nixpkgs.latestPackages = lib.mkOption { default = []; };
    };

    config = {
        ###
        # DIRTY HACK
        # This will fetch latest packages on each rebuild, whatever channel you are at
        nixpkgs.overlays = [
            (self: super:
                let latestPkgs = import (fetchTarball https://github.com/nixos/nixpkgs-channels/archive/nixpkgs-unstable.tar.gz) {
                        config.allowUnfree = true;
                    };
                in lib.genAttrs config.nixpkgs.latestPackages (pkg: latestPkgs."${pkg}")
            )
        ];
        # END DIRTY HACK
        ###

        environment.systemPackages = [ pkgs.vscode ];

        system.activationScripts.fix-vscode-extensions = {
            text = ''
                EXT_DIR=${config.vscode.homeDir}/.vscode/extensions
                mkdir -p $EXT_DIR
                chown ${config.vscode.user}:users $EXT_DIR
                for x in ${lib.concatMapStringsSep " " toString config.vscode.extensions}; do
                    ln -sf $x/share/vscode/extensions/* $EXT_DIR/
                done
                chown -R ${config.vscode.user}:users $EXT_DIR
            '';
            deps = [];
        };
    };
}

Then in configuration.nix you add lines:

  imports = [
    ...
    ./vscode.nix
  ];
  vscode.user = "danbst";
  vscode.homeDir = "/home/danbst";
  vscode.extensions = with pkgs.vscode-extensions; [
    ms-vscode.cpptools
  ];
  nixpkgs.latestPackages = [
    "vscode"
    "vscode-extensions"
  ];

And rebuild. What it does:

  • fetches latest nixpkgs-unstable channel on each rebuild
  • uses vscode and extensions from that channel, so up-to-date ASAP
  • links cpptools directly into ~/.vscode/extensions, which fools VScode to think it actually installed this ext
3 Likes

This is really cool, including the dirty hack.
I honestly feel like this should be documented somewhere, I was not able to find such a great how to!

Is it also possible to get the same result with only user overlays?

“Dirty hack” will never become “official”. For example, I’ve had to revert the abovementioned “fetch latest VScode” hack from my configuration. Why? It crashed sometimes, probably due to essential impurities: glibc mismatch, protocols mismatch, kernel mismatch, whatnot. I think, it crashed only because of this update, not because VSCode has an actual bug.

Another example is calibre - latest calibre crashes for me on opening file, but I suppose it crashes only for me. And will no longer crash after system update.

It is also nightmare to debug such cases, so better to stick to packages coherent with system.

Then the problem persists, how can I install packages over nix, while also maintaining the “ease of use” of querying for packages in the editor and installing them?

Oh, no. latest Nix package and VScode extensions live in different worlds. You still can use second part of my solution above. Just remove nixpkgs.latestPackages part from configuration.

Yes, you’ll have slightly outdated VSCode. But maybe better to switch to nixos-unstable channel and update your system periodically (nixos-rebuild switch --upgrade or nixos-rebuild boot --upgrade && reboot)? This would solve your other issue as well.

You might want to add your voice to Microsoft/vscode/issues/27972: Include support for system-wide mandatory/default settings which would allow us to trivially have both nix provided extensions (system wide) and user extensions (managed by vscode).

In the meantime, one might be tempted to write a companion helper to vscode-with-extensions which instead of passing the --extension-dir argument in its wrapper, dynamically creates symlinks to each nix provided extension before launching vscode.

3 Likes

This approach seems to be superseded by vscode-with-extensions, which is just a wrapper with the correct arguments, right?

I’m asking because home-manager seems the use the approach here, not the wrapper package. This leads to the extensions failing, vscode 1.43 breaks default extensions · Issue #83288 · NixOS/nixpkgs · GitHub and home-manager 20.03 seems to bork vscode extension paths · Issue #1260 · nix-community/home-manager · GitHub.

1 Like

Indeed, this approach is documented here, too: Visual Studio Code - NixOS Wiki

However, for me, this isn’t quite enough yet, since I still have to specify each extension by hand and commit hash, which is terrible for updating.

hm, so I am currently trying to install vscode with some extensions like so

environment.systemPackages = with pkgs; [
    (vscode-with-extensions.override {
      vscodeExtensions = with vscode-extensions; [
        "vscodevim.vim"
        "ms-dotnettools.csharp"
      ];
    })
]

but still getting this error:

When do you get that error? Immediately at boot or when you are trying to do something?

The error occurs when I try to start vscode.

Have you also added vscode itself to environment.systemPackages?

If so, seems it should be working properly. Might try enabling extensions in your config one-by-one to see if one is causing the error.

Could also try managing VS Code and extensions through home-manager.

1 Like

I think that the problem is that you’re installing vscode under systemPackages, which correlates to the root user.

The first part of the error seems to indicate that vscode is trying to write to a location in the nix store, which is not writable other than by the root user. I’m not sure if the second part of the error is actually accurate.

If you move the vscode config from environment.systemPackages to user.users.<your user>.packages, then it should work for your user. That’s how I currently have it set up. Or otherwise use home manager, which I’m about to try instead. I think one difference between using the user section of environment.config vs home manager would be the ability to rollback easily, but not entirely sure actually.

If you have multiple users and want to have vscode globally, then I think you’ll have to create shell aliases or something to launch code and supply a --user-data-dir to the command line, which should cause vscode to store the extensions, config, and other things in a directory in the user’s own home folder.

1 Like

Yes, while adding vscode to user packages the error was gone and it worked. Nevertheless, when adding extensions to my config vscode crashed on start up without an error. Weird. Using home-manager did the trick and it is working also with vscode extensions. Thanks for your replies.