den
1{lib, ...}: {
2 den.aspects.dev.provides.vscode.homeManager = {
3 config,
4 osConfig,
5 pkgs,
6 ...
7 }: let
8 bask-pkg = pkgs.vscode;
9
10 chromiumCLA = lib.optionals isLinux ([
11 "--ozone-platform-hint=auto"
12 "--enable-wayland-ime"
13 "--wayland-text-input-version=3"
14 ]
15 ++ lib.optionals (!isPlasma) [
16 # Fix https://github.com/microsoft/vscode/issues/187338
17 "--password-store=gnome-libsecret"
18 ]);
19
20 isLinux = pkgs.stdenv.hostPlatform.isLinux;
21 isPlasma = isLinux && (osConfig.services.desktopManager.plasma6.enable or false);
22
23 pkg = bask-pkg.override {
24 commandLineArgs =
25 (lib.lists.concatLists [
26 ["--extensions-dir" extensionsDir]
27 ["--user-data-dir" userDataDir]
28 ["--locale" "zh-cn"]
29 ])
30 ++ chromiumCLA;
31 };
32
33 # Below VSCode paths are copied from home-manager's vscode module source
34 vscodePname = bask-pkg.pname;
35 vscodeConfigDir =
36 {
37 "vscode" = "Code";
38 "vscode-insiders" = "Code - Insiders";
39 "vscodium" = "VSCodium";
40 "openvscode-server" = "OpenVSCode Server";
41 "windsurf" = "Windsurf";
42 "cursor" = "Cursor";
43 }.${
44 vscodePname
45 };
46 vscodeExtensionDir =
47 {
48 "vscode" = "vscode";
49 "vscode-insiders" = "vscode-insiders";
50 "vscodium" = "vscode-oss";
51 "openvscode-server" = "openvscode-server";
52 "windsurf" = "windsurf";
53 "cursor" = "cursor";
54 }.${
55 vscodePname
56 };
57 vscodeUserDir =
58 if pkgs.stdenv.hostPlatform.isDarwin
59 then "${config.home.homeDirectory}/Library/Application Support/${vscodeConfigDir}/User"
60 else "${config.xdg.configHome}/${vscodeConfigDir}/User";
61
62 homeDir = config.home.homeDirectory;
63 dataDir = config.xdg.dataHome;
64 userDataDir = "${dataDir}/${vscodePname}/data";
65 extensionsDir = "${dataDir}/${vscodePname}/extensions";
66
67 vscodeSetupScript = pkgs.writeShellApplication {
68 name = "vscode-setup";
69 runtimeInputs = with pkgs; [coreutils gnutar jq];
70 text = let
71 userSrc = vscodeUserDir;
72 userDst = "${userDataDir}/User";
73 extSrc = ".${vscodeExtensionDir}/extensions";
74 extDst = extensionsDir;
75
76 dirsToPreserve = [
77 "workspaceStorage"
78 "History"
79 ];
80 backupCmds = builtins.concatStringsSep "\n" (map (dir: ''
81 if [ -e "${userDst}/${dir}" ]; then
82 echo "Backing up data/User/${dir}..."
83 mv "${userDst}/${dir}" "/tmp/vscode-${dir}-$$"
84 fi
85 '')
86 (dirsToPreserve ++ ["globalStorage"]));
87 restoreCmds = builtins.concatStringsSep "\n" (map (dir: ''
88 if [ -e "/tmp/vscode-${dir}-$$" ]; then
89 echo "Restoring data/User/${dir}..."
90 mv "/tmp/vscode-${dir}-$$" "${userDst}/${dir}"
91 fi
92 '')
93 dirsToPreserve);
94 in ''
95 set -eu
96
97 ${backupCmds}
98
99 echo "Cleaning old directories..."
100 rm -rf "${userDst}"
101 rm -rf "${extDst}"
102
103 mkdir -p "${userDataDir}"
104 mkdir -p "${extDst}"
105
106 echo "Copying user settings from ${userSrc}..."
107 cp -r --dereference --no-preserve=mode,ownership ${userSrc} "${userDst}"
108
109 echo "Copying extensions from ${extSrc}..."
110 tar -h -C "${extSrc}" -cf - . | tar -C "${extDst}" -xf - --no-same-owner --no-same-permissions --mode='u=rX,go=rX'
111 chmod u+w -R "${extDst}" 2>/dev/null || true
112
113 ${restoreCmds}
114
115 echo "Restoring and merging data/User/globalStorage..."
116 if [ -e "/tmp/vscode-globalStorage-$$" ]; then
117 cp -rT "/tmp/vscode-globalStorage-$$" "${userDst}/globalStorage"
118
119 src_storage_json="${userSrc}/globalStorage/storage.json"
120 dst_storage_json="${userDst}/globalStorage/storage.json"
121
122 if [ -f "$src_storage_json" ] && [ -f "$dst_storage_json" ]; then
123 echo "Merging data/globalStorage/storage.json with new data..."
124 merged_json=$(mktemp)
125 jq -s '.[0] * .[1]' "$dst_storage_json" "$src_storage_json" > "$merged_json"
126 mv "$merged_json" "$dst_storage_json"
127 echo "Merge complete."
128 else
129 echo "Skipping storage.json merge: one or both files do not exist."
130 fi
131 else
132 echo "No backed-up globalStorage found to restore."
133 fi
134
135 echo "VSCode setup complete."
136 '';
137 };
138 in {
139 programs.vscode = {
140 enable = true;
141 package = pkg;
142 mutableExtensionsDir = false;
143 };
144
145 systemd.user.services.vscode-setup = lib.mkIf pkgs.stdenv.hostPlatform.isLinux {
146 Unit = {
147 Description = "VSCode Setup service";
148 After = ["graphical-session-pre.target"];
149 Wants = ["graphical-session-pre.target"];
150 };
151 Install.WantedBy = ["graphical-session.target"];
152 Service = {
153 Type = "oneshot";
154 UMask = "0022";
155 ExecStart = lib.getExe vscodeSetupScript;
156 };
157 };
158
159 # TODO: Test on real macOS system.
160 # For now, it just a simple mirror of the Linux service.
161 launchd.agents.vscode-setup = lib.mkIf pkgs.stdenv.hostPlatform.isDarwin {
162 enable = true;
163 config = {
164 Program = [lib.getExe vscodeSetupScript];
165 RunAtLoad = true;
166 StandardOutPath = "${homeDir}/Library/Logs/vscode-setup.log";
167 StandardErrorPath = "${homeDir}/Library/Logs/vscode-setup.log";
168 WorkingDirectory = homeDir;
169 };
170 };
171 };
172}