Improve logic for finding VSCode / Cursor settings files (#32721)

* Fixes a bug where for Cursor, `config_dir()` (Zed's config dir) was
being used instead of `dirs::config_dir` (`~/.config` /
`$XDG_CONFIG_HOME`).

* Adds support for windows, before it was using the user profile folder
+ `/.config` which is incorrect.

* Now looks using a variety of product names - `["Code", "Code - OSS",
"Code Dev", "Code - OSS Dev", "code-oss-dev", "VSCodium"]`.

* Now shows settings path that was read before confirming import.

Including this path in the confirmation modal is a bit ugly (making it
link-styled and clickable would be nice), but I think it's better to
include it now that it is selecting the first match of a list of
candidate paths:


![image](https://github.com/user-attachments/assets/ceada4c2-96a6-4a84-a188-a1d93521ab26)

Release Notes:

- Added more settings file locations to check for VS Code / Cursor
settings import.
This commit is contained in:
Michael Sloan 2025-06-14 21:39:54 -06:00 committed by GitHub
parent afa70034d5
commit a5ceef35fa
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 138 additions and 59 deletions

View file

@ -1,5 +1,6 @@
//! Paths to locations used by Zed.
use std::env;
use std::path::{Path, PathBuf};
use std::sync::OnceLock;
@ -106,6 +107,7 @@ pub fn data_dir() -> &'static PathBuf {
}
})
}
/// Returns the path to the temp directory used by Zed.
pub fn temp_dir() -> &'static PathBuf {
static TEMP_DIR: OnceLock<PathBuf> = OnceLock::new();
@ -426,32 +428,74 @@ pub fn global_ssh_config_file() -> &'static Path {
Path::new("/etc/ssh/ssh_config")
}
/// Returns the path to the vscode user settings file
pub fn vscode_settings_file() -> &'static PathBuf {
static LOGS_DIR: OnceLock<PathBuf> = OnceLock::new();
let rel_path = "Code/User/settings.json";
LOGS_DIR.get_or_init(|| {
if cfg!(target_os = "macos") {
home_dir()
.join("Library/Application Support")
.join(rel_path)
} else {
home_dir().join(".config").join(rel_path)
}
})
/// Returns candidate paths for the vscode user settings file
pub fn vscode_settings_file_paths() -> Vec<PathBuf> {
let mut paths = vscode_user_data_paths();
for path in paths.iter_mut() {
path.push("User/settings.json");
}
paths
}
/// Returns the path to the cursor user settings file
pub fn cursor_settings_file() -> &'static PathBuf {
static LOGS_DIR: OnceLock<PathBuf> = OnceLock::new();
let rel_path = "Cursor/User/settings.json";
LOGS_DIR.get_or_init(|| {
if cfg!(target_os = "macos") {
/// Returns candidate paths for the cursor user settings file
pub fn cursor_settings_file_paths() -> Vec<PathBuf> {
let mut paths = cursor_user_data_paths();
for path in paths.iter_mut() {
path.push("User/settings.json");
}
paths
}
fn vscode_user_data_paths() -> Vec<PathBuf> {
// https://github.com/microsoft/vscode/blob/23e7148cdb6d8a27f0109ff77e5b1e019f8da051/src/vs/platform/environment/node/userDataPath.ts#L45
const VSCODE_PRODUCT_NAMES: &[&str] = &[
"Code",
"Code - OSS",
"VSCodium",
"Code Dev",
"Code - OSS Dev",
"code-oss-dev",
];
let mut paths = Vec::new();
if let Ok(portable_path) = env::var("VSCODE_PORTABLE") {
paths.push(Path::new(&portable_path).join("user-data"));
}
if let Ok(vscode_appdata) = env::var("VSCODE_APPDATA") {
for product_name in VSCODE_PRODUCT_NAMES {
paths.push(Path::new(&vscode_appdata).join(product_name));
}
}
for product_name in VSCODE_PRODUCT_NAMES {
add_vscode_user_data_paths(&mut paths, product_name);
}
paths
}
fn cursor_user_data_paths() -> Vec<PathBuf> {
let mut paths = Vec::new();
add_vscode_user_data_paths(&mut paths, "Cursor");
paths
}
fn add_vscode_user_data_paths(paths: &mut Vec<PathBuf>, product_name: &str) {
if cfg!(target_os = "macos") {
paths.push(
home_dir()
.join("Library/Application Support")
.join(rel_path)
} else {
config_dir().join(rel_path)
.join(product_name),
);
} else if cfg!(target_os = "windows") {
if let Some(data_local_dir) = dirs::data_local_dir() {
paths.push(data_local_dir.join(product_name));
}
})
if let Some(data_dir) = dirs::data_dir() {
paths.push(data_dir.join(product_name));
}
} else {
paths.push(
dirs::config_dir()
.unwrap_or(home_dir().join(".config"))
.join(product_name),
);
}
}