Configuring VSCode with Nix on macOS

Nov 24 2024
4 minutes

Welcome back to the Nix on Mac series! By the end of this post, you’ll be able to fully configure VSCode through your Nix flake via home-manager, which we set up in part 2. This includes:

  • Custom keybindings and settings
  • Installing themes and extensions from a nixpkgs overlay
  • Properly aliasing VSCode and other macOS applications to /Applications for Spotlight

Without further ado, let’s dive in!

Installing VSCode

Nix won’t install non-free software like VSCode without you opting in. Add this line to your nix-darwin configuration module:

configuration = { pkgs, ... }: {
    # ...
    
    nixpkgs.config.allowUnfree = true;
    
    # ...
};

After that, installing VSCode is just one line of code in our home-manager config:

homeconfig = { pkgs, ... }: {
    programs.vscode = {
        enable = true;
    };
};

I’m sure many of you who are following along are using VSCode to edit your flake.nix file. I should note that installing VSCode through home-manager will likely blow away your existing settings when you call switch. Now would be a good time to back up your existing settings and extension list so you can replicate your setup from within Nix.

VSCode will be installed in /Users/$USER/Applications/Home Manager Apps/Notably, this isn’t inside the normal /Applications folder since home-manager can only install programs under your home directory. Even stranger, Spotlight can’t pick up our alias! We’ll come back and fix this at the end of the post. after running switch.

If you’ve previously installed VSCode, go ahead and dump the copy from /Applications in the trash. Where we’re going, we don’t need roads globally installed programs!

Editor Settings and Keybindings

You can add user settings and keybindings pretty easily:

programs.vscode = {
    enable = true;

    userSettings = {
        # This property will be used to generate settings.json:
        # https://code.visualstudio.com/docs/getstarted/settings#_settingsjson
        "editor.formatOnSave" = true;
    };
    keybindings = [
        # See https://code.visualstudio.com/docs/getstarted/keybindings#_advanced-customization
        {
            key = "shift+cmd+j";
            command = "workbench.action.focusActiveEditorGroup";
            when = "terminalFocus";
        }
    ];
};

It almost looks like JSON! And in fact, converting Nix types to and from JSON is part of the Nix standard library.

Extensions

A huge part of VSCode is the bountiful extension ecosystem. home-manager lets you install extensions similarly to how it installs packages. The full VSCode marketplace isn’t present in nixpkgs by default, so we’ll need to install an overlay.

Nixpkgs overlays let you override and add new entries to nixpkgs. We can add the nix-vscode-extensions overlay by adding a line to our nix-darwin configuration:

{
  # ...
  inputs = {
    # ...
    nix-vscode-extensions.url = "github:nix-community/nix-vscode-extensions";
  };

  outputs =
    inputs@{ self
    , nixpkgs
    , nix-darwin
    , home-manager
    , mac-app-util
    , nix-vscode-extensions
    }:
    let
      configuration = { pkgs, ... }: {
        # ...
        nixpkgs.overlays = [
          nix-vscode-extensions.overlays.default
        ];
        # ...
      };

      homeconfig = { pkgs, ...}: {
        # ...
        programs.vscode {
          # ...

          userSettings = {
            # ...
            "workbench.colorTheme" = "Dracula Theme";
          };

          # ...

          extensions = [
            pkgs.vscode-marketplace.jnoortheen.nix-ide
            pkgs.vscode-marketplace.dracula-theme.theme-dracula
          ];
        }
      }
  # ...

vscode-marketplace is one of the properties that the nix-vscode-extensions overlay added to nixpkgs. Any VSCode extension in the marketplace should be accessible from pkgs.vscode-marketplace.$AUTHOR.$EXTENSION, where $AUTHOR.$EXTENSION is the same as the itemName property in the extension’s URL on the extension marketplace website.

Cleaning up lists of attributes with the with clause

Writing pkgs.vscode-marketplace in front of every extension will get tedious as as your list of extenions gets longer and make the list harder to read. Luckily Nix has a language construct to help with this: in front of any expression, you can type with <attribute set> to bring all attributes within the attribute set into scope. Our extensions list can look like:

extensions = with pkgs.vscode-marketplace; [
    jnoortheen.nix-ide
    dracula-theme.theme-dracula
];

Much cleaner!

Playing nice with Spotlight

You might have noticed that the version of VSCode that Nix installs doesn’t show up in Spotlight. Why is that? It’s unfortunately pretty simple: All artifacts that Nix and home-manager add to your system are symbolic links, and Spotlight won’t index symlinks. There’s a very long thread about this on GitHub if you’re interested in reading. From the various options discussed there, mac-app-util is the easiest way to get Spotlight working as expected.

We can add it as another input at the top of our flake and then modify our flake output to load mac-app-util in both nix-darwin and home-manager:

{
    description = "My system configuration";
    inputs = {
        # ...
        mac-app-util.url = "github:hraban/mac-app-util";
    };

    # ...

    in
    {
      darwinConfigurations."$HOSTNAME" = nix-darwin.lib.darwinSystem {
        modules = [
          # ...
          mac-app-util.darwinModules.default
          home-manager.darwinModules.home-manager
          {
            # ...
            home-manager.sharedModules = [
                mac-app-util.homeManagerModules.default
            ];
            # ...
          }
        ];
      };
    }
}

After running switch, VSCode should be properly copied to /Users/$USER/Applications/Home Manager Trampolines and should be available the next time Spotlight refreshes.

Stepping Back

We just included someone’s third-party Nix utility in our flake to do some custom behavior when Nix installs macOS GUI apps. You probably didn’t notice that mac-app-util is written in Common Lisp. I’ve never written Common Lisp, and my guess is you haven’t either. Even so, with fewer than five lines of code we were able make use of some code that someone wrote in their favorite niche programming language without needing to figure out how to build or run that code. Nix handled it all for us! I, for one, think that’s pretty amazing.

Conclusion

Our Nix config is now able to fully configure and manage VSCode, including installing extensions and themes. home-manager has just as deep an integration with many other utilities. If you haven’t already definitely go through and check out home-manager’s config options to find out what you can do with your favorite program.

You can find the full flake.nix for this installment on GitHub here. See you in the next one!

nix vscode nix-on-mac editors