diff --git a/crates/languages/src/python.rs b/crates/languages/src/python.rs index a35608b473..b1f5706b69 100644 --- a/crates/languages/src/python.rs +++ b/crates/languages/src/python.rs @@ -106,6 +106,24 @@ impl LspAdapter for PythonLspAdapter { Self::SERVER_NAME.clone() } + async fn initialization_options( + self: Arc, + _: &dyn Fs, + _: &Arc, + ) -> Result> { + // Provide minimal initialization options + // Virtual environment configuration will be handled through workspace configuration + Ok(Some(json!({ + "python": { + "analysis": { + "autoSearchPaths": true, + "useLibraryCodeForTypes": true, + "autoImportCompletions": true + } + } + }))) + } + async fn check_if_user_installed( &self, delegate: &dyn LspAdapterDelegate, @@ -128,9 +146,10 @@ impl LspAdapter for PythonLspAdapter { let path = node_modules_path.join(NODE_MODULE_RELATIVE_SERVER_PATH); + let env = delegate.shell_env().await; Some(LanguageServerBinary { path: node, - env: None, + env: Some(env), arguments: server_binary_arguments(&path), }) } @@ -151,7 +170,7 @@ impl LspAdapter for PythonLspAdapter { &self, latest_version: Box, container_dir: PathBuf, - _: &dyn LspAdapterDelegate, + delegate: &dyn LspAdapterDelegate, ) -> Result { let latest_version = latest_version.downcast::().unwrap(); let server_path = container_dir.join(SERVER_PATH); @@ -163,9 +182,10 @@ impl LspAdapter for PythonLspAdapter { ) .await?; + let env = delegate.shell_env().await; Ok(LanguageServerBinary { path: self.node.binary_path().await?, - env: None, + env: Some(env), arguments: server_binary_arguments(&server_path), }) } @@ -174,7 +194,7 @@ impl LspAdapter for PythonLspAdapter { &self, version: &(dyn 'static + Send + Any), container_dir: &PathBuf, - _: &dyn LspAdapterDelegate, + delegate: &dyn LspAdapterDelegate, ) -> Option { let version = version.downcast_ref::().unwrap(); let server_path = container_dir.join(SERVER_PATH); @@ -192,9 +212,10 @@ impl LspAdapter for PythonLspAdapter { if should_install_language_server { None } else { + let env = delegate.shell_env().await; Some(LanguageServerBinary { path: self.node.binary_path().await.ok()?, - env: None, + env: Some(env), arguments: server_binary_arguments(&server_path), }) } @@ -203,9 +224,11 @@ impl LspAdapter for PythonLspAdapter { async fn cached_server_binary( &self, container_dir: PathBuf, - _: &dyn LspAdapterDelegate, + delegate: &dyn LspAdapterDelegate, ) -> Option { - get_cached_server_binary(container_dir, &self.node).await + let mut binary = get_cached_server_binary(container_dir, &self.node).await?; + binary.env = Some(delegate.shell_env().await); + Some(binary) } async fn process_completions(&self, items: &mut [lsp::CompletionItem]) { @@ -308,22 +331,64 @@ impl LspAdapter for PythonLspAdapter { .and_then(|s| s.settings.clone()) .unwrap_or_default(); - // If python.pythonPath is not set in user config, do so using our toolchain picker. + // If we have a detected toolchain, configure Pyright to use it if let Some(toolchain) = toolchain { if user_settings.is_null() { user_settings = Value::Object(serde_json::Map::default()); } let object = user_settings.as_object_mut().unwrap(); - if let Some(python) = object + + let interpreter_path = toolchain.path.to_string(); + + // 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("bin/activate").exists() + || venv_dir.join("Scripts/activate.bat").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 + 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 + // Get or create the python section + let python = object .entry("python") .or_insert(Value::Object(serde_json::Map::default())) .as_object_mut() - { - python - .entry("pythonPath") - .or_insert(Value::String(toolchain.path.into())); - } + .unwrap(); + + // Set both pythonPath and defaultInterpreterPath for compatibility + python.insert( + "pythonPath".to_owned(), + Value::String(interpreter_path.clone()), + ); + python.insert( + "defaultInterpreterPath".to_owned(), + Value::String(interpreter_path), + ); } + user_settings }) }