vscodium and nix develop

25 jun 2024

motivation

this post is about how to connect visual studio code with proper language support and code completion for rust to a nix based toolchain inside nixos-wsl. this setup let’s me surf the code like a html document!

the open source edition of microsoft visual studio code called vscodium is being used. it features an implementation for remote code access via ssh, which is also open source, and is called open-remote-ssh.

note: for visual studio code this plugin called ms-vscode-remote.remote-ssh is closed source but should work for this setup, too.

what is needed

  • host: windows 10
  • guest: nixos-wsl installation
    • working ssh configuration from host
    • setup nixos-vscode-server properly: vscodium
    • klick, an example rust project

supported nix development environments

there are several popular similar concepts:

experimental setup

to learn about the vscodium implementation of ssh remote work i’ve created this shell-wrapper on the nixos-wsl instance.

nixos-wsl configuration

{ config, lib, pkgs, fetchurl, fetchFromGitHub, ... }:

let
  rust_overlay = import (fetchTarball "https://github.com/oxalica/rust-overlay/archive/master.tar.gz");
  pkgs = import <nixpkgs> { overlays = [ rust_overlay ]; };
  rust = pkgs.rust-bin.stable.latest.default.override {
    extensions = [ "rustfmt" "clippy" ];
    targets = [
      "wasm32-unknown-unknown"
    ];
  };
in

{
  imports = [
    # include NixOS-WSL modules
    <nixos-wsl/modules>
    (fetchTarball "https://github.com/nix-community/nixos-vscode-server/tarball/master")
  ];
  users.users.nixos.shell = /home/nixos/klick/bash-wrapper.sh;

  nix.settings.experimental-features = [ "nix-command" "flakes" ];
  wsl.enable = true;
  services.vscode-server.enable = true;
  environment.systemPackages = with pkgs; [ vim git tig dfc wineWowPackages.base jq direnv htop ];
  services.openssh = {
    enable = true;
  };
  system.stateVersion = "23.05"; # Did you read the comment?
}

/home/nixos/klick/bash-wrapper.sh

#!/run/current-system/sw/bin/bash
# NOTE: each change requires a 'nixos-rebuild switch' run to become active

cd /home/nixos/klick

is_interactive() {
    # A shell is considered interactive if it has a terminal associated with standard input
    if [ -t 0 ]; then
        return 0
    else
        return 1
    fi
}

# Main execution starts here
# Check if the shell is interactive
if is_interactive; then
    # If the shell is interactive, call bash in interactive mode
    #exec /run/current-system/sw/bin/bash
    echo "interactive"
    exec /run/current-system/sw/bin/nix develop --command /run/current-system/sw/bin/bash
else
    # If the shell is non-interactive, pass all arguments to bash
    #exec /run/current-system/sw/bin/bash "$@"
    echo "none interactive"
    exec /run/current-system/sw/bin/nix develop --command /run/current-system/sw/bin/bash -c "$@"
fi

how to use

once everything is installed, do a ssh nixos and check that you are inside the nix develop environment:

[nixos@nixos:~/klick]$ rustc --version
rustc 1.79.0 (129f3b996 2024-06-10)

then add this ssh remote into vscodium’s remote extension and load the project.

inside the development console to the same rustc --version check.

future potential

using vscodium we could create a way to automatically load the nix development environment, if detected.

how SHELL is detected

on nixos this is set as default:

declare -x SHELL="/nix/store/agkxax48k35wdmkhmmija2i2sxg8i7ny-bash-5.2p26/bin/bash"

and resolved using this:

  • vscode does the shell resolution internally at microsoft/vscode
  • by default it will use the shell from $SHELL but if that’s unset it will use node’s userInfo().shell which will read /etc/passwd, see microsoft/vscode.

see also discussion at https://github.com/jeanp413/open-remote-ssh/issues/159

proposal

using a .vscode-nix.toml file in the source directory we could help vscode to use the correct shell environment:

  • modify the vscode resolver to check for a file named .vscode-nix.toml, and if found, use that command inside the SHELL

example .vscode-nix.toml

# .vscode-nix.toml
# This configuration file helps Visual Studio Code determine the shell to use for different Nix environments.

# Specify the shell to use for Nix Flake's 'nix develop'
[shell]
name = "nix flake"
command = "nix develop"

# Specify the shell to use for Devenv
#[shell]
#name = "devenv"
#command = "devenv shell"

# Specify the shell to use for Direnv
#[shell]
#name = "direnv"
#command = "direnv allow && direnv exec . $SHELL"

# Specify the shell to use for Nix Shell
#[shell]
#name = "nix-shell"
#command = "nix-shell"

# Specify the shell to use for flox.dev
#[shell]
#name = "flox"
#command = "flox activate"

so when vscode wants to start a shell, it will call the command instead of /nix/store/agkxax48k35wdmkhmmija2i2sxg8i7ny-bash-5.2p26/bin/bash.

summary

this hack shows the huge potential of using nix develop in combination to powerful remote IDEs.

in fact, the proposed idea here is very similar to direnv-in-vscode, except it does not require a specific program to be running on the destination host or a specific vscode plugin as direnv-vscode.

i created a feature request for vscode: https://github.com/microsoft/vscode/issues/218361.

article source