mirror of
https://github.com/l0ng-ai/openidea.git
synced 2026-11-06 16:08:05 +00:00
With a PHP file focused, the status bar shows the PHP level Intelephense checks against (`PHP 7.4`); the tooltip says where it comes from (the settings, config.platform.php or require.php). Clicking it opens Auto (with the composer.json version) and 5.6 to 8.5; picking one writes the same project setting as Settings › Languages & Frameworks › PHP, so diagnostics follow without reopening the project. The widget is hidden for other files. For a file of a linked workspace project, the level comes from that project's settings and the choices are disabled. The menu is also the action "PHP Language Level". Closes #1091 Co-authored-by: DanielADK <18214958+DanielADK@users.noreply.github.com>
168 lines
8.3 KiB
Rust
168 lines
8.3 KiB
Rust
use ide_core::{ChangeSet, NullSink, RequestId, TextChange};
|
|
use ide_host::{Host, HostConfig, ops};
|
|
use ide_workspace::workspace::Workspace;
|
|
use std::{
|
|
sync::{Arc, Mutex},
|
|
time::Duration,
|
|
};
|
|
|
|
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
|
async fn linked_projects_share_navigation_but_keep_files_repositories_and_run_directories() {
|
|
let temp = tempfile::tempdir().unwrap();
|
|
let base = dunce::canonicalize(temp.path()).unwrap();
|
|
let roots = vec![base.join("frontend"), base.join("backend")];
|
|
for (index, root) in roots.iter().enumerate() {
|
|
std::fs::create_dir(root).unwrap();
|
|
assert!(std::process::Command::new("git").args(["init", "-q"]).current_dir(root).status().unwrap().success());
|
|
std::fs::write(root.join("same.txt"), format!("shared needle {index}\n")).unwrap();
|
|
std::fs::write(root.join("package.json"), r#"{"scripts":{"test":"echo test"}}"#).unwrap();
|
|
}
|
|
let root = base.join("workspace");
|
|
Workspace::save(&root, "Full stack", vec![roots[0].clone()], true).unwrap();
|
|
let host = 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,
|
|
});
|
|
std::fs::write(root.join("internal-needle.txt"), "needle").unwrap();
|
|
host.trust.decide(&root, true, false).unwrap();
|
|
let project = host.open_project("main", &root).unwrap();
|
|
assert_eq!(ops::project::info(&project).name, "Full stack");
|
|
assert_eq!(ops::project::attach_workspace(&host, "main", &roots[1]).unwrap().as_ref(), root.as_path());
|
|
ops::project::attach_workspace(&host, "main", &roots[1]).unwrap();
|
|
assert_eq!(project.content_roots(), roots);
|
|
assert!(project.index.wait_idle(Duration::from_secs(10)));
|
|
assert!(project.index.find_files("internal-needle", 100).is_empty());
|
|
assert!(project.index.find_files("workspace.json", 100).is_empty());
|
|
assert_eq!(project.index.find_files("same.txt", 100).len(), 2);
|
|
for _ in 0..500 {
|
|
if project.repos().all().len() == 2 {
|
|
break;
|
|
}
|
|
tokio::time::sleep(Duration::from_millis(10)).await;
|
|
}
|
|
assert_eq!(project.repos().all().len(), 2);
|
|
let configs = ops::run::configs(&host, "main").unwrap();
|
|
for member in &roots {
|
|
assert!(configs.iter().any(|c| c.cwd.as_ref().is_some_and(|cwd| &**cwd == member)));
|
|
}
|
|
let mut ids = configs.iter().map(|c| c.id.clone()).collect::<Vec<_>>();
|
|
ids.sort();
|
|
ids.dedup();
|
|
assert_eq!(ids.len(), configs.len());
|
|
let a = ops::doc::open(&host, "main", roots[0].join("same.txt")).await.unwrap();
|
|
let b = ops::doc::open(&host, "main", roots[1].join("same.txt")).await.unwrap();
|
|
assert_ne!(a.id, b.id);
|
|
ops::doc::edit(
|
|
&host,
|
|
"main",
|
|
a.id,
|
|
a.version,
|
|
ChangeSet { changes: vec![TextChange { from: 0, to: 0, insert: "unsaved ".into() }] },
|
|
)
|
|
.unwrap();
|
|
let hits = Arc::new(Mutex::new(Vec::new()));
|
|
let out = hits.clone();
|
|
let query = serde_json::from_value(serde_json::json!({"pattern":"needle", "isRegex":false, "caseSensitive":false,"wholeWord":false,"include":[],"exclude":[],"scope":{"kind":"project"},"maxMatches":100})).unwrap();
|
|
ops::search::start(
|
|
&host,
|
|
"main",
|
|
RequestId::next(),
|
|
query,
|
|
Arc::new(move |event| {
|
|
if let ops::search::SearchEvent::File { file } = event {
|
|
out.lock().unwrap().push(file);
|
|
}
|
|
}),
|
|
)
|
|
.await
|
|
.unwrap();
|
|
assert_eq!(hits.lock().unwrap().len(), 2);
|
|
let found = hits.lock().unwrap().iter().map(|f| f.rel_path.clone()).collect::<Vec<_>>();
|
|
assert!(found.contains(&"frontend/same.txt".to_owned()));
|
|
assert!(found.contains(&"backend/same.txt".to_owned()));
|
|
let updated = ops::project::save_workspace(&host, "main", "Frontend", vec![roots[0].clone()]).unwrap();
|
|
assert_eq!(updated.workspace.unwrap().projects, vec![roots[0].clone()]);
|
|
assert_eq!(project.content_roots(), vec![roots[0].clone()]);
|
|
assert!(project.documents.text_of(&roots[0].join("same.txt")).unwrap().starts_with("unsaved"));
|
|
assert!(roots[1].join("same.txt").exists());
|
|
assert!(!roots[1].join(".openidea").exists());
|
|
let invalid = base.join("invalid");
|
|
std::fs::create_dir_all(invalid.join(".openidea")).unwrap();
|
|
std::fs::write(invalid.join(ide_workspace::workspace::FILE), "broken").unwrap();
|
|
assert!(host.open_project("main", &invalid).is_err());
|
|
assert!(Arc::ptr_eq(&host.project("main").unwrap(), &project));
|
|
host.open_project("main", &roots[0]).unwrap();
|
|
let automatic = ops::project::attach_workspace(&host, "main", &roots[1]).unwrap();
|
|
assert_eq!(host.project("main").unwrap().root, roots[0]);
|
|
let automatic = host.open_project("main", &automatic).unwrap();
|
|
assert_eq!(automatic.content_roots(), roots);
|
|
assert_eq!(ops::project::info(&automatic).name, "frontend");
|
|
assert!(automatic.root.starts_with(base.join("data/workspaces")));
|
|
assert!(automatic.index.wait_idle(Duration::from_secs(10)));
|
|
assert!(automatic.index.find_files("workspace.json", 100).is_empty());
|
|
let updated = ops::project::save_workspace(&host, "main", "frontend", vec![roots[1].clone()]).unwrap();
|
|
assert_eq!(updated.workspace.unwrap().projects, vec![roots[1].clone()]);
|
|
assert_eq!(automatic.root, updated.root.as_ref());
|
|
assert!(automatic.index.wait_idle(Duration::from_secs(10)));
|
|
assert_eq!(automatic.index.find_files("same.txt", 100).len(), 1);
|
|
host.close_project("main");
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
|
async fn php_language_level_follows_the_member_of_the_file() {
|
|
use ide_sdk::php::{PhpLanguageLevel, PhpLevelSource};
|
|
let temp = tempfile::tempdir().unwrap();
|
|
let base = dunce::canonicalize(temp.path()).unwrap();
|
|
let (api, legacy) = (base.join("api"), base.join("legacy"));
|
|
for (member, composer) in [
|
|
(&api, r#"{"require": {"php": "^7.4"}}"#),
|
|
(&legacy, r#"{"require": {"php": "^7.4"}, "config": {"platform": {"php": "8.0.5"}}}"#),
|
|
] {
|
|
std::fs::create_dir(member).unwrap();
|
|
std::fs::write(member.join("composer.json"), composer).unwrap();
|
|
}
|
|
let root = base.join("workspace");
|
|
Workspace::save(&root, "PHP", vec![api.clone(), legacy.clone()], true).unwrap();
|
|
std::fs::write(root.join("composer.json"), r#"{"require": {"php": "^8.2"}}"#).unwrap();
|
|
let config_dir = base.join("config");
|
|
ide_workspace::SettingsStore::load(&config_dir, Some(&legacy))
|
|
.set(ide_workspace::SettingsScope::Project, "languages.php.languageLevel", "8.3".into())
|
|
.unwrap();
|
|
let host = Host::new(HostConfig {
|
|
config_dir,
|
|
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,
|
|
});
|
|
host.trust.decide(&root, true, false).unwrap();
|
|
host.open_project("main", &root).unwrap();
|
|
let found = |version: &str, source| Some(PhpLanguageLevel { version: version.into(), source });
|
|
let level = async |path: std::path::PathBuf| ops::sdk::sdk_php_language_level(&host, "main", path).await.unwrap();
|
|
|
|
let level_of_api = level(api.join("src/Invoice.php")).await;
|
|
assert_eq!(level_of_api.effective, found("7.4.0", PhpLevelSource::Require));
|
|
assert_eq!(level_of_api.auto, level_of_api.effective);
|
|
assert_eq!(level_of_api.member.as_deref(), Some(api.as_path()));
|
|
// The member's own settings, not the workspace's.
|
|
let level_of_legacy = level(legacy.join("index.php")).await;
|
|
assert_eq!(level_of_legacy.effective, found("8.3.0", PhpLevelSource::Settings));
|
|
assert_eq!(level_of_legacy.auto, found("8.0.5", PhpLevelSource::Platform));
|
|
// Outside every member: the workspace's own.
|
|
let level_elsewhere = level(base.join("elsewhere.php")).await;
|
|
assert_eq!(level_elsewhere.effective, found("8.2.0", PhpLevelSource::Require));
|
|
assert_eq!(level_elsewhere.member, None);
|
|
host.close_project("main");
|
|
}
|