mirror of
https://github.com/l0ng-ai/openidea.git
synced 2026-11-08 17:02:17 +00:00
CI / Changes (push) Successful in 1s
Language smoke / Language smoke (go) (push) Failing after 6s
Language smoke / Language smoke (java) (push) Failing after 5s
Language smoke / Language smoke (kotlin) (push) Failing after 5s
Language smoke / Language smoke (python) (push) Failing after 4s
Language smoke / Language smoke (typescript) (push) Failing after 4s
Language smoke / Language smoke (rust) (push) Failing after 5s
CI / Rust (ubuntu-22.04) (push) Failing after 43s
CI / Plugins (SDK, official, fixtures) (push) Failing after 1m19s
CI / Remote server (linux musl) (push) Failing after 1m32s
CI / Cargo deny (push) Successful in 54s
CI / E2E (push) Successful in 15m57s
CI / Frontend (push) Successful in 24m49s
CI / Rust (macos-latest) (push) Canceled after 0s
CI / Rust (windows-latest) (push) Canceled after 0s
* fix(build): scope Kotlin bridge to linked Maven modules * fix(run): resolve nested Go modules for context runs * fix(gradle): discover nested builds and select compatible JVMs * fix(workspace): share project discovery and module ownership * fix(run): keep root toolchains for nested modules and resolve them once per module - Python run configurations of a nested module without its own virtualenv use the closest one up to the project root. - Gradle builds and runs fall back to the JDK matching the project's Java version when the Gradle version is unknown. - Module-local interpreters and toolchains are resolved lazily, once per owning module. - Tests build expected paths per segment so they hold on Windows.
182 lines
7.9 KiB
Rust
182 lines
7.9 KiB
Rust
//! LightEdit: a window editing loose files without a project.
|
|
|
|
use ide_core::{ChangeSet, NullSink, TextChange};
|
|
use ide_host::{Host, HostConfig, ops};
|
|
use std::sync::Arc;
|
|
use std::time::Duration;
|
|
|
|
fn host(base: &std::path::Path) -> Arc<Host> {
|
|
Arc::new(Host::new(HostConfig {
|
|
config_dir: base.join("config"),
|
|
data_dir: base.join("data"),
|
|
local_data_dir: base.join("data"),
|
|
languages: Arc::new(ide_language::LanguageRegistry::load(None).unwrap()),
|
|
syntax: Arc::default(),
|
|
sink_for: Box::new(|_| Arc::new(NullSink)),
|
|
owner: |w| w,
|
|
runtime: tokio::runtime::Handle::current(),
|
|
project_environment: false,
|
|
}))
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
|
async fn edits_loose_files_without_project_services() {
|
|
let tmp = tempfile::tempdir().unwrap();
|
|
let tmp_root = dunce::canonicalize(tmp.path()).unwrap();
|
|
let (a_dir, b_dir) = (tmp_root.join("a"), tmp_root.join("b"));
|
|
std::fs::create_dir_all(a_dir.join(".git")).unwrap();
|
|
std::fs::create_dir_all(&b_dir).unwrap();
|
|
std::fs::write(a_dir.join("one.py"), "print(1)\n").unwrap();
|
|
std::fs::write(b_dir.join("two.rs"), "fn main() {}\n").unwrap();
|
|
let host = host(&tmp_root.join("app"));
|
|
|
|
let light = host.open_light("main", &a_dir.join("one.py")).unwrap();
|
|
assert!(light.light);
|
|
assert_eq!(light.root, a_dir);
|
|
assert!(host.open_light("main", &b_dir).is_err(), "a folder is not opened for LightEdit");
|
|
|
|
let doc = ops::doc::open(&host, "main", a_dir.join("one.py")).await.unwrap();
|
|
assert!(doc.text.starts_with("print"));
|
|
let changes = ChangeSet { changes: vec![TextChange { from: 0, to: 0, insert: "#\n".into() }] };
|
|
let version = ops::doc::edit(&host, "main", doc.id, doc.version, changes).unwrap();
|
|
assert!(version > doc.version);
|
|
ops::doc::save(&host, "main", doc.id).await.unwrap();
|
|
assert_eq!(std::fs::read_to_string(a_dir.join("one.py")).unwrap(), "#\nprint(1)\n");
|
|
|
|
// More files join the same session, wherever they are.
|
|
let again = host.open_light("main", &b_dir.join("two.rs")).unwrap();
|
|
assert!(Arc::ptr_eq(&light, &again));
|
|
ops::doc::open(&host, "main", b_dir.join("two.rs")).await.unwrap();
|
|
|
|
assert!(light.index.wait_idle(Duration::from_secs(10)));
|
|
assert_eq!(light.index.status().files, 0, "nothing is indexed");
|
|
assert!(light.lsp.servers().is_empty(), "no language servers");
|
|
assert!(light.repos().is_empty(), "no repositories");
|
|
|
|
// Opening the folder as a project replaces the LightEdit session.
|
|
let project = host.open_project("main", &a_dir).unwrap();
|
|
assert!(!project.light);
|
|
assert!(!Arc::ptr_eq(&project, &light));
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
|
async fn light_windows_ignore_project_scoped_settings() {
|
|
let tmp = tempfile::tempdir().unwrap();
|
|
let dir = dunce::canonicalize(tmp.path()).unwrap().join("loose");
|
|
std::fs::create_dir_all(&dir).unwrap();
|
|
std::fs::write(dir.join("notes.txt"), "x\n").unwrap();
|
|
let host = host(&tmp.path().join("app"));
|
|
host.open_light("main", &dir.join("notes.txt")).unwrap();
|
|
let applied = ops::project::set_settings(
|
|
&host,
|
|
"main",
|
|
ide_workspace::settings::SettingsScope::Project,
|
|
"general.projectName",
|
|
"\"loose\"",
|
|
)
|
|
.unwrap();
|
|
assert!(!applied.project_scope);
|
|
assert!(!dir.join(".openidea").exists(), "no project settings next to the file");
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
|
async fn light_windows_stay_restricted_when_folder_trust_changes() {
|
|
let tmp = tempfile::tempdir().unwrap();
|
|
let root = tmp.path().join("loose");
|
|
let other = tmp.path().join("other");
|
|
std::fs::create_dir_all(&root).unwrap();
|
|
std::fs::create_dir_all(&other).unwrap();
|
|
std::fs::write(root.join("one.py"), "print(1)\n").unwrap();
|
|
std::fs::write(other.join("two.js"), "const value = 1;\n").unwrap();
|
|
let host = host(&tmp.path().join("app"));
|
|
host.global_settings
|
|
.set(ide_workspace::SettingsScope::Global, "languages.prettier.prefer", serde_json::json!(true))
|
|
.unwrap();
|
|
let light = host.open_light("main", &root.join("one.py")).unwrap();
|
|
assert!(!light.is_trusted());
|
|
assert!(light.require_trust().is_err());
|
|
|
|
host.set_project_trust(&root, true, false).await.unwrap();
|
|
assert!(!light.is_trusted());
|
|
let trusted_folder = host.open_light("second", &root.join("one.py")).unwrap();
|
|
assert!(!trusted_folder.is_trusted());
|
|
let again = host.open_light("main", &other.join("two.js")).unwrap();
|
|
assert!(Arc::ptr_eq(&light, &again));
|
|
assert!(!again.is_trusted());
|
|
host.set_project_trust(&root, true, true).await.unwrap();
|
|
assert!(!light.is_trusted());
|
|
assert!(!trusted_folder.is_trusted());
|
|
for command in ["sdk_detect", "sdk_locate_tools", "notebook_execute", "notebook_restart", "run_start"] {
|
|
assert!(host.authorize_command("main", command).is_err(), "{command}");
|
|
}
|
|
|
|
let doc = ops::doc::open(&host, "main", other.join("two.js")).await.unwrap();
|
|
let error = ops::lsp::lsp_format(
|
|
&host,
|
|
"main",
|
|
doc.id,
|
|
None,
|
|
ide_lsp::types::FormatOptions { tab_size: 2, insert_spaces: true, on_save: false, tool: None },
|
|
)
|
|
.await
|
|
.unwrap_err();
|
|
assert_eq!(error.kind, ide_core::error::ErrorKind::Unavailable);
|
|
assert!(error.message.contains("Restricted Mode"));
|
|
|
|
host.set_project_trust(&root, false, false).await.unwrap();
|
|
assert!(!light.is_trusted());
|
|
assert!(!trusted_folder.is_trusted());
|
|
host.set_project_trust(&root, true, false).await.unwrap();
|
|
let project = host.open_project("main", &root).unwrap();
|
|
assert!(!project.light);
|
|
assert!(project.is_trusted());
|
|
assert!(project.require_trust().is_ok());
|
|
host.shutdown().await;
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
|
async fn light_notebooks_open_without_interpreter_detection_or_execution() {
|
|
let tmp = tempfile::tempdir().unwrap();
|
|
let root = tmp.path().join("loose");
|
|
let bin = root.join(".venv").join(if cfg!(windows) { "Scripts" } else { "bin" });
|
|
std::fs::create_dir_all(&bin).unwrap();
|
|
std::fs::write(root.join(".venv/pyvenv.cfg"), "home = python\n").unwrap();
|
|
#[cfg(unix)]
|
|
{
|
|
use std::os::unix::fs::PermissionsExt;
|
|
let python = bin.join("python");
|
|
std::fs::write(&python, "#!/bin/sh\nprintf probed > \"$0.probed\"\nprintf 'Python 3.12.0\\n'\n").unwrap();
|
|
std::fs::set_permissions(&python, std::fs::Permissions::from_mode(0o755)).unwrap();
|
|
}
|
|
let file = root.join("analysis.ipynb");
|
|
std::fs::write(
|
|
&file,
|
|
r#"{"cells":[{"id":"cell","cell_type":"code","source":["print(1)"],"metadata":{},"outputs":[],"execution_count":null}],"metadata":{},"nbformat":4,"nbformat_minor":5}"#,
|
|
)
|
|
.unwrap();
|
|
let host = host(&tmp.path().join("app"));
|
|
host.open_light("main", &file).unwrap();
|
|
let notebook = ops::notebook::open(&host, "main", file.to_string_lossy().into()).await.unwrap();
|
|
assert!(ops::notebook::save(&host, "main", notebook.id.clone()).await.is_ok());
|
|
let error = ops::sdk::sdk_detect(&host, "main", ide_sdk::SdkKind::Python).await.unwrap_err();
|
|
assert!(error.message.contains("Restricted Mode"));
|
|
assert!(ops::sdk::sdk_python_effective(&host, "main", None).is_err());
|
|
assert!(ops::sdk::sdk_locate_tools(&host, "main", vec!["prettier".into()], Some(root)).await.is_err());
|
|
let error = ops::notebook::restart(&host, "main", notebook.id.clone()).await.unwrap_err();
|
|
assert!(error.message.contains("Restricted Mode"));
|
|
let error = ops::notebook::execute(
|
|
&host,
|
|
"main",
|
|
notebook.id,
|
|
vec![notebook.cells[0].id.clone()],
|
|
None,
|
|
ide_core::RequestId(849),
|
|
ops::Channel::new(|_| {}),
|
|
)
|
|
.await
|
|
.unwrap_err();
|
|
assert!(error.message.contains("Restricted Mode"));
|
|
assert!(!bin.join("python.probed").exists());
|
|
host.shutdown().await;
|
|
}
|