This commit is contained in:
Lukas Wirth 2025-08-14 10:28:41 +02:00
parent ac449d0ca8
commit eb086e3689

View file

@ -22,6 +22,7 @@ use project::lsp_store::language_server_settings;
use serde_json::{Value, json}; use serde_json::{Value, json};
use smol::lock::OnceCell; use smol::lock::OnceCell;
use std::cmp::Ordering; use std::cmp::Ordering;
use std::ffi::OsStr;
use parking_lot::Mutex; use parking_lot::Mutex;
use std::str::FromStr; use std::str::FromStr;
@ -335,36 +336,20 @@ impl LspAdapter for PythonLspAdapter {
} }
let object = user_settings.as_object_mut().unwrap(); let object = user_settings.as_object_mut().unwrap();
let interpreter_path = toolchain.path.to_string(); let interpreter_path = toolchain.path.as_ref();
// Detect if this is a virtual environment let (venv_path, venv) = detect_venv(&**adapter, interpreter_path);
if let Some(interpreter_dir) = Path::new(&interpreter_path).parent() { if let Some(venv) = venv {
if let Some(venv_dir) = interpreter_dir.parent() { object.insert(
// Check if this looks like a virtual environment "venv".to_owned(),
if venv_dir.join("pyvenv.cfg").exists() Value::String(venv.to_string_lossy().into_owned()),
|| venv_dir.join("bin/activate").exists() );
|| venv_dir.join("Scripts/activate.bat").exists() }
{ if let Some(venv_path) = venv_path {
// Set venvPath and venv at the root level object.insert(
// This matches the format of a pyrightconfig.json file "venvPath".to_string(),
if let Some(parent) = venv_dir.parent() { Value::String(venv_path.to_string_lossy().into_owned()),
// Use relative path if the venv is inside the workspace );
let venv_path = if parent == adapter.worktree_root_path() {
".".to_string()
} else {
parent.to_string_lossy().into_owned()
};
object.insert("venvPath".to_string(), Value::String(venv_path));
}
if let Some(venv_name) = venv_dir.file_name() {
object.insert(
"venv".to_owned(),
Value::String(venv_name.to_string_lossy().into_owned()),
);
}
}
}
} }
// Always set the python interpreter path // Always set the python interpreter path
@ -378,11 +363,11 @@ impl LspAdapter for PythonLspAdapter {
// Set both pythonPath and defaultInterpreterPath for compatibility // Set both pythonPath and defaultInterpreterPath for compatibility
python.insert( python.insert(
"pythonPath".to_owned(), "pythonPath".to_owned(),
Value::String(interpreter_path.clone()), Value::String(interpreter_path.to_owned()),
); );
python.insert( python.insert(
"defaultInterpreterPath".to_owned(), "defaultInterpreterPath".to_owned(),
Value::String(interpreter_path), Value::String(interpreter_path.to_owned()),
); );
} }
@ -1027,6 +1012,12 @@ const BINARY_DIR: &str = if cfg!(target_os = "windows") {
"bin" "bin"
}; };
const ACTIVATE_PATH: &str = if cfg!(target_os = "windows") {
"Scripts/activate.bat"
} else {
"bin/activate"
};
#[async_trait(?Send)] #[async_trait(?Send)]
impl LspAdapter for PyLspAdapter { impl LspAdapter for PyLspAdapter {
fn name(&self) -> LanguageServerName { fn name(&self) -> LanguageServerName {
@ -1523,36 +1514,20 @@ impl LspAdapter for BasedPyrightLspAdapter {
} }
let object = user_settings.as_object_mut().unwrap(); let object = user_settings.as_object_mut().unwrap();
let interpreter_path = toolchain.path.to_string(); let interpreter_path = toolchain.path.as_ref();
// Detect if this is a virtual environment let (venv_path, venv) = detect_venv(&**adapter, interpreter_path);
if let Some(interpreter_dir) = Path::new(&interpreter_path).parent() { if let Some(venv) = venv {
if let Some(venv_dir) = interpreter_dir.parent() { object.insert(
// Check if this looks like a virtual environment "venv".to_owned(),
if venv_dir.join("pyvenv.cfg").exists() Value::String(venv.to_string_lossy().into_owned()),
|| venv_dir.join("bin/activate").exists() );
|| venv_dir.join("Scripts/activate.bat").exists() }
{ if let Some(venv_path) = venv_path {
// Set venvPath and venv at the root level object.insert(
// This matches the format of a pyrightconfig.json file "venvPath".to_string(),
if let Some(parent) = venv_dir.parent() { Value::String(venv_path.to_string_lossy().into_owned()),
// Use relative path if the venv is inside the workspace );
let venv_path = if parent == adapter.worktree_root_path() {
".".to_string()
} else {
parent.to_string_lossy().into_owned()
};
object.insert("venvPath".to_string(), Value::String(venv_path));
}
if let Some(venv_name) = venv_dir.file_name() {
object.insert(
"venv".to_owned(),
Value::String(venv_name.to_string_lossy().into_owned()),
);
}
}
}
} }
// Always set the python interpreter path // Always set the python interpreter path
@ -1566,11 +1541,11 @@ impl LspAdapter for BasedPyrightLspAdapter {
// Set both pythonPath and defaultInterpreterPath for compatibility // Set both pythonPath and defaultInterpreterPath for compatibility
python.insert( python.insert(
"pythonPath".to_owned(), "pythonPath".to_owned(),
Value::String(interpreter_path.clone()), Value::String(interpreter_path.to_owned()),
); );
python.insert( python.insert(
"defaultInterpreterPath".to_owned(), "defaultInterpreterPath".to_owned(),
Value::String(interpreter_path), Value::String(interpreter_path.to_owned()),
); );
} }
@ -1583,6 +1558,38 @@ impl LspAdapter for BasedPyrightLspAdapter {
} }
} }
/// Detect if the interpreter path belongs to a virtual environment
fn detect_venv<'p>(
adapter: &dyn LspAdapterDelegate,
interpreter_path: &'p str,
) -> (Option<&'p Path>, Option<&'p OsStr>) {
let mut venv_path = None;
let mut venv = None;
// Detect if this is a virtual environment
if let Some(interpreter_dir) = Path::new(interpreter_path).parent() {
if let Some(venv_dir) = interpreter_dir.parent() {
// Check if this looks like a virtual environment
if venv_dir.join("pyvenv.cfg").exists() || venv_dir.join(ACTIVATE_PATH).exists() {
// Set venvPath and venv at the root level
// This matches the format of a pyrightconfig.json file
if let Some(parent) = venv_dir.parent() {
// Use relative path if the venv is inside the workspace
venv_path = Some(if parent == adapter.worktree_root_path() {
Path::new(".")
} else {
parent
});
}
if let Some(venv_name) = venv_dir.file_name() {
venv = Some(venv_name);
}
}
}
}
(venv_path, venv)
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use gpui::{AppContext as _, BorrowAppContext, Context, TestAppContext}; use gpui::{AppContext as _, BorrowAppContext, Context, TestAppContext};