home-manager/modules/programs/vscode.nix
Emily 9f9e277b60 treewide: remove now-redundant lib.mdDoc calls
These (and the `*MD` functions apart from `literalMD`) are now no-ops
in nixpkgs and serve no purpose other than to add additional noise and
potentially mislead people into thinking unmarked DocBook documentation
will still be accepted.

Note that if backporting changes including documentation to 23.05,
the `mdDoc` calls will need to be re-added.

To reproduce this commit, run:

    $ NIX_PATH=nixpkgs=flake:nixpkgs/e7e69199f0372364a6106a1e735f68604f4c5a25 \
      nix shell nixpkgs#coreutils \
      -c find . -name '*.nix' \
      -exec nix run -- github:emilazy/nix-doc-munge/98dadf1f77351c2ba5dcb709a2a171d655f15099 \
      --strip {} +
    $ ./format
2023-07-17 18:49:09 +01:00

292 lines
8.6 KiB
Nix

{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.programs.vscode;
vscodePname = cfg.package.pname;
vscodeVersion = cfg.package.version;
jsonFormat = pkgs.formats.json { };
configDir = {
"vscode" = "Code";
"vscode-insiders" = "Code - Insiders";
"vscodium" = "VSCodium";
}.${vscodePname};
extensionDir = {
"vscode" = "vscode";
"vscode-insiders" = "vscode-insiders";
"vscodium" = "vscode-oss";
}.${vscodePname};
userDir = if pkgs.stdenv.hostPlatform.isDarwin then
"Library/Application Support/${configDir}/User"
else
"${config.xdg.configHome}/${configDir}/User";
configFilePath = "${userDir}/settings.json";
tasksFilePath = "${userDir}/tasks.json";
keybindingsFilePath = "${userDir}/keybindings.json";
snippetDir = "${userDir}/snippets";
# TODO: On Darwin where are the extensions?
extensionPath = ".${extensionDir}/extensions";
extensionJson = pkgs.vscode-utils.toExtensionJson cfg.extensions;
extensionJsonFile = pkgs.writeTextFile {
name = "extensions-json";
destination = "/share/vscode/extensions/extensions.json";
text = extensionJson;
};
mergedUserSettings = cfg.userSettings
// optionalAttrs (!cfg.enableUpdateCheck) { "update.mode" = "none"; }
// optionalAttrs (!cfg.enableExtensionUpdateCheck) {
"extensions.autoCheckUpdates" = false;
};
in {
imports = [
(mkChangedOptionModule [ "programs" "vscode" "immutableExtensionsDir" ] [
"programs"
"vscode"
"mutableExtensionsDir"
] (config: !config.programs.vscode.immutableExtensionsDir))
];
options = {
programs.vscode = {
enable = mkEnableOption "Visual Studio Code";
package = mkOption {
type = types.package;
default = pkgs.vscode;
defaultText = literalExpression "pkgs.vscode";
example = literalExpression "pkgs.vscodium";
description = ''
Version of Visual Studio Code to install.
'';
};
enableUpdateCheck = mkOption {
type = types.bool;
default = true;
description = ''
Whether to enable update checks/notifications.
'';
};
enableExtensionUpdateCheck = mkOption {
type = types.bool;
default = true;
description = ''
Whether to enable update notifications for extensions.
'';
};
userSettings = mkOption {
type = jsonFormat.type;
default = { };
example = literalExpression ''
{
"files.autoSave" = "off";
"[nix]"."editor.tabSize" = 2;
}
'';
description = ''
Configuration written to Visual Studio Code's
{file}`settings.json`.
'';
};
userTasks = mkOption {
type = jsonFormat.type;
default = { };
example = literalExpression ''
{
version = "2.0.0";
tasks = [
{
type = "shell";
label = "Hello task";
command = "hello";
}
];
}
'';
description = ''
Configuration written to Visual Studio Code's
{file}`tasks.json`.
'';
};
keybindings = mkOption {
type = types.listOf (types.submodule {
options = {
key = mkOption {
type = types.str;
example = "ctrl+c";
description = "The key or key-combination to bind.";
};
command = mkOption {
type = types.str;
example = "editor.action.clipboardCopyAction";
description = "The VS Code command to execute.";
};
when = mkOption {
type = types.nullOr (types.str);
default = null;
example = "textInputFocus";
description = "Optional context filter.";
};
# https://code.visualstudio.com/docs/getstarted/keybindings#_command-arguments
args = mkOption {
type = types.nullOr (jsonFormat.type);
default = null;
example = { direction = "up"; };
description = "Optional arguments for a command.";
};
};
});
default = [ ];
example = literalExpression ''
[
{
key = "ctrl+c";
command = "editor.action.clipboardCopyAction";
when = "textInputFocus";
}
]
'';
description = ''
Keybindings written to Visual Studio Code's
{file}`keybindings.json`.
'';
};
extensions = mkOption {
type = types.listOf types.package;
default = [ ];
example = literalExpression "[ pkgs.vscode-extensions.bbenoist.nix ]";
description = ''
The extensions Visual Studio Code should be started with.
'';
};
mutableExtensionsDir = mkOption {
type = types.bool;
default = true;
example = false;
description = ''
Whether extensions can be installed or updated manually
or by Visual Studio Code.
'';
};
languageSnippets = mkOption {
type = jsonFormat.type;
default = { };
example = {
haskell = {
fixme = {
prefix = [ "fixme" ];
body = [ "$LINE_COMMENT FIXME: $0" ];
description = "Insert a FIXME remark";
};
};
};
description = "Defines user snippets for different languages.";
};
globalSnippets = mkOption {
type = jsonFormat.type;
default = { };
example = {
fixme = {
prefix = [ "fixme" ];
body = [ "$LINE_COMMENT FIXME: $0" ];
description = "Insert a FIXME remark";
};
};
description = "Defines global user snippets.";
};
};
};
config = mkIf cfg.enable {
home.packages = [ cfg.package ];
home.file = mkMerge [
(mkIf (mergedUserSettings != { }) {
"${configFilePath}".source =
jsonFormat.generate "vscode-user-settings" mergedUserSettings;
})
(mkIf (cfg.userTasks != { }) {
"${tasksFilePath}".source =
jsonFormat.generate "vscode-user-tasks" cfg.userTasks;
})
(mkIf (cfg.keybindings != [ ])
(let dropNullFields = filterAttrs (_: v: v != null);
in {
"${keybindingsFilePath}".source =
jsonFormat.generate "vscode-keybindings"
(map dropNullFields cfg.keybindings);
}))
(mkIf (cfg.extensions != [ ]) (let
subDir = "share/vscode/extensions";
# Adapted from https://discourse.nixos.org/t/vscode-extensions-setup/1801/2
toPaths = ext:
map (k: { "${extensionPath}/${k}".source = "${ext}/${subDir}/${k}"; })
(if ext ? vscodeExtUniqueId then
[ ext.vscodeExtUniqueId ]
else
builtins.attrNames (builtins.readDir (ext + "/${subDir}")));
in if cfg.mutableExtensionsDir then
mkMerge (concatMap toPaths cfg.extensions
++ lib.optional (lib.versionAtLeast vscodeVersion "1.74.0") {
# Whenever our immutable extensions.json changes, force VSCode to regenerate
# extensions.json with both mutable and immutable extensions.
"${extensionPath}/.extensions-immutable.json" = {
text = extensionJson;
onChange = ''
$DRY_RUN_CMD rm $VERBOSE_ARG -f ${extensionPath}/{extensions.json,.init-default-profile-extensions}
$VERBOSE_ECHO "Regenerating VSCode extensions.json"
$DRY_RUN_CMD ${getExe cfg.package} --list-extensions > /dev/null
'';
};
})
else {
"${extensionPath}".source = let
combinedExtensionsDrv = pkgs.buildEnv {
name = "vscode-extensions";
paths = cfg.extensions
++ lib.optional (lib.versionAtLeast vscodeVersion "1.74.0")
extensionJsonFile;
};
in "${combinedExtensionsDrv}/${subDir}";
}))
(mkIf (cfg.globalSnippets != { })
(let globalSnippets = "${snippetDir}/global.code-snippets";
in {
"${globalSnippets}".source =
jsonFormat.generate "user-snippet-global.code-snippets"
cfg.globalSnippets;
}))
(lib.mapAttrs' (language: snippet:
lib.nameValuePair "${snippetDir}/${language}.json" {
source = jsonFormat.generate "user-snippet-${language}.json" snippet;
}) cfg.languageSnippets)
];
};
}