mirror of
https://github.com/l0ng-ai/openidea.git
synced 2026-11-06 08:07:49 +00:00
fix(python): name the project interpreter in the status bar wherever it lives (#1461)
An interpreter that detection doesn't look for (a virtualenv in a subfolder such as backend/.venv, or one set by path) was missing from sdk_detect, so the status bar and interpreter menu showed its raw path. sdk_detect for Python now adds the project's interpreter when detection didn't find it, named by the same rules, and a project-local .venv in a subfolder is named after that folder. Fixes #1428
This commit is contained in:
@@ -23,8 +23,26 @@ pub struct LocatedTools {
|
||||
/// Installed interpreters / toolchains of `kind`; project-local ones first.
|
||||
pub async fn sdk_detect(host: &Host, window: &str, kind: SdkKind) -> IdeResult<Vec<Sdk>> {
|
||||
host.require_trust(window)?;
|
||||
let root = host.project(window).ok().map(|p| p.root.clone());
|
||||
tokio::task::spawn_blocking(move || ide_sdk::detect(kind, root.as_deref())).await.map_err(join_err)
|
||||
let project = host.project(window).ok();
|
||||
let root = project.as_ref().map(|p| p.root.clone());
|
||||
// The interpreter the project uses is a choice even where detection doesn't look (a
|
||||
// virtualenv in a subfolder, one picked by path), so it shows by name and version.
|
||||
let current = match (&project, kind) {
|
||||
(Some(p), SdkKind::Python) => ide_sdk::apply::python_interpreter(&p.root, &p.settings.effective()),
|
||||
_ => None,
|
||||
};
|
||||
tokio::task::spawn_blocking(move || {
|
||||
let mut sdks = ide_sdk::detect(kind, root.as_deref());
|
||||
if let Some(path) = current
|
||||
&& !sdks.iter().any(|s| *s.path == *path)
|
||||
&& let Some(sdk) = ide_sdk::python_interpreter_sdk(&path, root.as_deref())
|
||||
{
|
||||
sdks.push(sdk);
|
||||
}
|
||||
sdks
|
||||
})
|
||||
.await
|
||||
.map_err(join_err)
|
||||
}
|
||||
|
||||
/// The interpreter the project uses: the configured one, else its virtualenv.
|
||||
|
||||
@@ -184,6 +184,12 @@ fn python(root: Option<&Path>) -> Vec<Sdk> {
|
||||
par_map(&candidates, |(path, source)| python_sdk(path, source, root))
|
||||
}
|
||||
|
||||
/// Interpreter `path` (one the project is set to, wherever it lives) as an SDK, named
|
||||
/// like the detected ones.
|
||||
pub fn python_interpreter_sdk(path: &Path, root: Option<&Path>) -> Option<Sdk> {
|
||||
python_sdk(path, if venv_of(path).is_some() { "venv" } else { "system" }, root)
|
||||
}
|
||||
|
||||
pub(crate) fn python_sdk(path: &Path, source: &str, root: Option<&Path>) -> Option<Sdk> {
|
||||
let version = query(path, &["--version"], VERSION_TIMEOUT)
|
||||
.and_then(|v| v.strip_prefix("Python ").map(str::to_owned))
|
||||
@@ -193,10 +199,11 @@ pub(crate) fn python_sdk(path: &Path, source: &str, root: Option<&Path>) -> Opti
|
||||
let label = match (&venv, source) {
|
||||
(Some(home), _) => {
|
||||
let dir = home.file_name().map(|n| n.to_string_lossy().into_owned()).unwrap_or_default();
|
||||
// `.venv` inside a project reads better as the project name, like PyCharm.
|
||||
// `.venv` inside a project reads better as the folder that holds it (the project,
|
||||
// or a member such as `backend`), like PyCharm.
|
||||
let owner = home.parent().and_then(|p| p.file_name()).map(|n| n.to_string_lossy().into_owned());
|
||||
match (dir.starts_with('.') || dir == "venv" || dir == "env", owner, root) {
|
||||
(true, Some(owner), Some(root)) if home.parent() == Some(root) => format!("Python {short} ({owner})"),
|
||||
(true, Some(owner), Some(root)) if home.starts_with(root) => format!("Python {short} ({owner})"),
|
||||
_ => format!("Python {short} ({dir})"),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ use serde::{Deserialize, Serialize};
|
||||
use specta::Type;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
pub use discover::{detect, jdk_sdk};
|
||||
pub use discover::{detect, jdk_sdk, python_interpreter_sdk};
|
||||
|
||||
/// What [`detect`] looks for.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, Type)]
|
||||
|
||||
@@ -76,6 +76,23 @@ fn project_venv_is_the_default_interpreter() {
|
||||
assert_eq!(python_interpreter(tmp.path(), &s).unwrap(), Path::new("/opt/py/bin/python3"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn an_interpreter_outside_detection_is_named_like_a_detected_one() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let root = tmp.path().join("shop");
|
||||
let member = fake_venv(&root.join("backend"), ".venv");
|
||||
let sdk = crate::python_interpreter_sdk(&member, Some(&root)).unwrap();
|
||||
assert_eq!(
|
||||
(sdk.name.as_str(), sdk.version.as_deref(), sdk.source.as_str()),
|
||||
("Python 3.12 (backend)", Some("3.12.1"), "venv")
|
||||
);
|
||||
let own = fake_venv(&root, ".venv");
|
||||
assert_eq!(crate::python_interpreter_sdk(&own, Some(&root)).unwrap().name, "Python 3.12 (shop)");
|
||||
// Outside the project the folder name says which one it is.
|
||||
let elsewhere = fake_venv(&tmp.path().join("other"), ".venv");
|
||||
assert_eq!(crate::python_interpreter_sdk(&elsewhere, Some(&root)).unwrap().name, "Python 3.12 (.venv)");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pyright_gets_python_path_and_venv_env() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
|
||||
Reference in New Issue
Block a user