mirror of
https://github.com/l0ng-ai/openidea.git
synced 2026-11-06 08:07:49 +00:00
feat(php): mark source and test roots from composer.json autoload (#1602)
Folders in composer.json's autoload.psr-4 become Sources Roots and those in autoload-dev.psr-4 Test Sources Roots (string or array values; paths into vendor/ or outside the folder are skipped). It runs before the package.json case, so a Laravel app's tests/ is credited to composer.json, and editing composer.json reruns detection.
This commit is contained in:
@@ -32,6 +32,7 @@ const BUILD_FILES: &[&str] = &[
|
||||
"Pipfile",
|
||||
"requirements.txt",
|
||||
"package.json",
|
||||
"composer.json",
|
||||
];
|
||||
|
||||
/// Folder names that are (or contain) conventional roots.
|
||||
@@ -214,6 +215,15 @@ fn module_roots(root: &Path, rel: &str, files: &[String], out: &mut Vec<SourceFo
|
||||
}
|
||||
}
|
||||
|
||||
// Before `package.json`: a Laravel app's `tests/` is the PHP test root.
|
||||
if has("composer.json")
|
||||
&& let Ok(text) = std::fs::read_to_string(dir.join("composer.json"))
|
||||
{
|
||||
for (sub, kind) in composer_roots(&text) {
|
||||
add(&sub, kind, "composer.json");
|
||||
}
|
||||
}
|
||||
|
||||
if has("package.json") {
|
||||
// A JVM build's `src` holds source sets, not JavaScript.
|
||||
if !has("pom.xml") && !GRADLE_FILES.iter().any(|f| has(f)) {
|
||||
@@ -230,6 +240,37 @@ fn module_roots(root: &Path, rel: &str, files: &[String], out: &mut Vec<SourceFo
|
||||
}
|
||||
}
|
||||
|
||||
/// `autoload.psr-4` folders as Source roots and `autoload-dev.psr-4` ones as Test roots, relative to
|
||||
/// the `composer.json` folder; paths that leave it or point into `vendor/` are left out.
|
||||
fn composer_roots(text: &str) -> Vec<(String, RootKind)> {
|
||||
let Ok(json) = serde_json::from_str::<serde_json::Value>(text) else { return Vec::new() };
|
||||
let mut out = Vec::new();
|
||||
for (section, kind) in [("autoload", RootKind::Source), ("autoload-dev", RootKind::Test)] {
|
||||
let Some(map) = json.get(section).and_then(|a| a.get("psr-4")).and_then(|m| m.as_object()) else { continue };
|
||||
for value in map.values() {
|
||||
let paths: Vec<&str> = match value {
|
||||
serde_json::Value::String(p) => vec![p.as_str()],
|
||||
serde_json::Value::Array(a) => a.iter().filter_map(|p| p.as_str()).collect(),
|
||||
_ => Vec::new(),
|
||||
};
|
||||
for path in paths {
|
||||
let parts: Vec<&str> = path.split(['/', '\\']).filter(|c| !c.is_empty() && *c != ".").collect();
|
||||
if path.starts_with(['/', '\\'])
|
||||
|| parts.iter().any(|c| *c == ".." || c.contains(':'))
|
||||
|| parts.first() == Some(&"vendor")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
let sub = parts.join("/");
|
||||
if !out.iter().any(|(p, _)| *p == sub) {
|
||||
out.push((sub, kind));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
/// Gradle's conventional `src/<set>/<lang>` folders that exist: `(set, lang)`.
|
||||
fn source_sets(dir: &Path) -> Vec<(String, String)> {
|
||||
let mut out = Vec::new();
|
||||
@@ -307,6 +348,47 @@ mod tests {
|
||||
assert!(roots.roots.iter().any(|r| r.path == "server/src" && r.kind == RootKind::Source));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn composer_psr4_folders_are_source_and_test_roots() {
|
||||
let dir = tree(&[
|
||||
"app/",
|
||||
"database/factories/",
|
||||
"database/seeders/",
|
||||
"lib/",
|
||||
"tests/",
|
||||
"vendor/acme/src/",
|
||||
"package.json",
|
||||
]);
|
||||
fs::write(
|
||||
dir.path().join("composer.json"),
|
||||
r#"{
|
||||
"autoload": { "psr-4": {
|
||||
"App\\": "app/",
|
||||
"Database\\Factories\\": "database/factories/",
|
||||
"Database\\Seeders\\": ["database/seeders/", "lib", "missing/"],
|
||||
"Vendored\\": "vendor/acme/src/",
|
||||
"Outside\\": "../elsewhere/"
|
||||
} },
|
||||
"autoload-dev": { "psr-4": { "Tests\\": "tests/" } }
|
||||
}"#,
|
||||
)
|
||||
.unwrap();
|
||||
let mut roots = summary(&detect(dir.path()));
|
||||
roots.sort();
|
||||
assert_eq!(
|
||||
roots,
|
||||
[
|
||||
"app=Source@composer.json",
|
||||
"database/factories=Source@composer.json",
|
||||
"database/seeders=Source@composer.json",
|
||||
"lib=Source@composer.json",
|
||||
"tests=Test@composer.json",
|
||||
]
|
||||
);
|
||||
assert!(affects_roots(Path::new("composer.json")));
|
||||
assert!(composer_roots("not json").is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn maven_conventional_roots_and_target() {
|
||||
let dir = tree(&[
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* Settings › Project Structure (IDEA's module "Sources" tab): the content
|
||||
* root's Sources / Tests / Resources / Test Resources / Excluded folders.
|
||||
* Roots detected from build files (`pom.xml`, `build.gradle`, `Cargo.toml`,
|
||||
* `go.mod`, `pyproject.toml`, `package.json`) apply until the user marks or
|
||||
* `go.mod`, `pyproject.toml`, `package.json`, `composer.json`) apply until the user marks or
|
||||
* unmarks the folder (`projectStructure.*`, project scope). Roots drive the
|
||||
* project tree icons, package inference for new classes, the "Project
|
||||
* Production / Test Files" scopes, and Excluded folders are skipped by the
|
||||
@@ -330,7 +330,7 @@ export function ProjectStructurePage() {
|
||||
<div className={styles.note}>
|
||||
<T>
|
||||
{
|
||||
"Folders are detected from build files (pom.xml, build.gradle, Cargo.toml, go.mod, pyproject.toml, package.json); marking or unmarking a folder overrides detection. Excluded folders are skipped by the Project view, Find in Files and the index (reopen the project to re-index)."
|
||||
"Folders are detected from build files (pom.xml, build.gradle, Cargo.toml, go.mod, pyproject.toml, package.json, composer.json); marking or unmarking a folder overrides detection. Excluded folders are skipped by the Project view, Find in Files and the index (reopen the project to re-index)."
|
||||
}
|
||||
</T>
|
||||
</div>
|
||||
|
||||
@@ -3810,7 +3810,7 @@
|
||||
"JDK Location": "Ubicación del JDK",
|
||||
"Downloading JDK": "Descargando JDK",
|
||||
"Use ; to separate name patterns, * for any number of symbols, ? for one.": "Use ; para separar patrones de nombres, * para cualquier cantidad de símbolos, ? para uno.",
|
||||
"Folders are detected from build files (pom.xml, build.gradle, Cargo.toml, go.mod, pyproject.toml, package.json); marking or unmarking a folder overrides detection. Excluded folders are skipped by the Project view, Find in Files and the index (reopen the project to re-index).": "Las carpetas se detectan a partir de archivos de compilación (pom.xml, build.gradle, Cargo.toml, go.mod, pyproject.toml, package.json); marcar o desmarcar una carpeta anula la detección. Las carpetas excluidas se omiten en la vista Proyecto, Buscar en archivos y el índice (vuelva a abrir el proyecto para reindexar).",
|
||||
"Folders are detected from build files (pom.xml, build.gradle, Cargo.toml, go.mod, pyproject.toml, package.json, composer.json); marking or unmarking a folder overrides detection. Excluded folders are skipped by the Project view, Find in Files and the index (reopen the project to re-index).": "Las carpetas se detectan a partir de archivos de compilación (pom.xml, build.gradle, Cargo.toml, go.mod, pyproject.toml, package.json, composer.json); marcar o desmarcar una carpeta anula la detección. Las carpetas excluidas se omiten en la vista Proyecto, Buscar en archivos y el índice (vuelva a abrir el proyecto para reindexar).",
|
||||
"Project virtualenv (auto-detect)": "Virtualenv del proyecto (detección automática)",
|
||||
"<No interpreter>": "<Sin intérprete>",
|
||||
"Package name or requirement, e.g. requests or django==5.1": "Nombre del paquete o requisito, p. ej., requests o django==5.1",
|
||||
|
||||
@@ -3676,7 +3676,7 @@
|
||||
"JDK Location": "Emplacement du JDK",
|
||||
"Downloading JDK": "Téléchargement du JDK",
|
||||
"Use ; to separate name patterns, * for any number of symbols, ? for one.": "Utilisez ; pour séparer les motifs de noms, * pour un nombre quelconque de caractères, ? pour un seul.",
|
||||
"Folders are detected from build files (pom.xml, build.gradle, Cargo.toml, go.mod, pyproject.toml, package.json); marking or unmarking a folder overrides detection. Excluded folders are skipped by the Project view, Find in Files and the index (reopen the project to re-index).": "Les dossiers sont détectés depuis les fichiers de build (pom.xml, build.gradle, Cargo.toml, go.mod, pyproject.toml, package.json) ; marquer ou démarquer un dossier remplace la détection. Les dossiers exclus sont ignorés par la vue Projet, Rechercher dans les fichiers et l'index (rouvrez le projet pour réindexer).",
|
||||
"Folders are detected from build files (pom.xml, build.gradle, Cargo.toml, go.mod, pyproject.toml, package.json, composer.json); marking or unmarking a folder overrides detection. Excluded folders are skipped by the Project view, Find in Files and the index (reopen the project to re-index).": "Les dossiers sont détectés depuis les fichiers de build (pom.xml, build.gradle, Cargo.toml, go.mod, pyproject.toml, package.json, composer.json) ; marquer ou démarquer un dossier remplace la détection. Les dossiers exclus sont ignorés par la vue Projet, Rechercher dans les fichiers et l'index (rouvrez le projet pour réindexer).",
|
||||
"Project virtualenv (auto-detect)": "Virtualenv du projet (détection automatique)",
|
||||
"<No interpreter>": "<Aucun interpréteur>",
|
||||
"Package name or requirement, e.g. requests or django==5.1": "Nom de paquet ou contrainte, p. ex. requests ou django==5.1",
|
||||
|
||||
@@ -3789,7 +3789,7 @@
|
||||
"JDK Location": "JDK 위치",
|
||||
"Downloading JDK": "JDK 다운로드 중",
|
||||
"Use ; to separate name patterns, * for any number of symbols, ? for one.": "이름 패턴은 ;로 구분하고, *는 임의 개수의 문자, ?는 한 문자와 일치합니다.",
|
||||
"Folders are detected from build files (pom.xml, build.gradle, Cargo.toml, go.mod, pyproject.toml, package.json); marking or unmarking a folder overrides detection. Excluded folders are skipped by the Project view, Find in Files and the index (reopen the project to re-index).": "폴더는 빌드 파일(pom.xml, build.gradle, Cargo.toml, go.mod, pyproject.toml, package.json)에서 감지되며, 폴더를 표시하거나 표시 해제하면 감지 결과가 재정의됩니다. 제외된 폴더는 프로젝트 뷰, 파일에서 찾기 및 인덱스에서 건너뜁니다(다시 인덱싱하려면 프로젝트를 다시 여세요).",
|
||||
"Folders are detected from build files (pom.xml, build.gradle, Cargo.toml, go.mod, pyproject.toml, package.json, composer.json); marking or unmarking a folder overrides detection. Excluded folders are skipped by the Project view, Find in Files and the index (reopen the project to re-index).": "폴더는 빌드 파일(pom.xml, build.gradle, Cargo.toml, go.mod, pyproject.toml, package.json, composer.json)에서 감지되며, 폴더를 표시하거나 표시 해제하면 감지 결과가 재정의됩니다. 제외된 폴더는 프로젝트 뷰, 파일에서 찾기 및 인덱스에서 건너뜁니다(다시 인덱싱하려면 프로젝트를 다시 여세요).",
|
||||
"Project virtualenv (auto-detect)": "프로젝트 virtualenv(자동 감지)",
|
||||
"<No interpreter>": "<인터프리터 없음>",
|
||||
"Package name or requirement, e.g. requests or django==5.1": "패키지 이름 또는 요구 사항, 예: requests 또는 django==5.1",
|
||||
|
||||
@@ -3779,7 +3779,7 @@
|
||||
"JDK Location": "Lokalizacja JDK",
|
||||
"Downloading JDK": "Pobieranie JDK",
|
||||
"Use ; to separate name patterns, * for any number of symbols, ? for one.": "Użyj ; do rozdzielania wzorców nazw, * dla dowolnej liczby znaków, ? dla jednego znaku.",
|
||||
"Folders are detected from build files (pom.xml, build.gradle, Cargo.toml, go.mod, pyproject.toml, package.json); marking or unmarking a folder overrides detection. Excluded folders are skipped by the Project view, Find in Files and the index (reopen the project to re-index).": "Foldery są wykrywane z plików budowania (pom.xml, build.gradle, Cargo.toml, go.mod, pyproject.toml, package.json); ręczne zaznaczenie lub odznaczenie foldera zastępuje automatyczne wykrywanie. Wykluczone foldery są pomijane w widoku Projektu, wyszukiwaniu w plikach oraz przez indeks (otwórz projekt ponownie, aby reindeksować).",
|
||||
"Folders are detected from build files (pom.xml, build.gradle, Cargo.toml, go.mod, pyproject.toml, package.json, composer.json); marking or unmarking a folder overrides detection. Excluded folders are skipped by the Project view, Find in Files and the index (reopen the project to re-index).": "Foldery są wykrywane z plików budowania (pom.xml, build.gradle, Cargo.toml, go.mod, pyproject.toml, package.json, composer.json); ręczne zaznaczenie lub odznaczenie foldera zastępuje automatyczne wykrywanie. Wykluczone foldery są pomijane w widoku Projektu, wyszukiwaniu w plikach oraz przez indeks (otwórz projekt ponownie, aby reindeksować).",
|
||||
"Project virtualenv (auto-detect)": "Środowisko wirtualne projektu (automatyczne wykrywanie)",
|
||||
"<No interpreter>": "<No interpreter>",
|
||||
"Download JDK…": "Pobierz JDK…",
|
||||
|
||||
@@ -3782,7 +3782,7 @@
|
||||
"JDK Location": "Local do JDK",
|
||||
"Downloading JDK": "Baixando JDK",
|
||||
"Use ; to separate name patterns, * for any number of symbols, ? for one.": "Use ; para separar padrões de nomes, * para qualquer quantidade de caracteres, ? para um.",
|
||||
"Folders are detected from build files (pom.xml, build.gradle, Cargo.toml, go.mod, pyproject.toml, package.json); marking or unmarking a folder overrides detection. Excluded folders are skipped by the Project view, Find in Files and the index (reopen the project to re-index).": "As pastas são detectadas a partir de arquivos de build (pom.xml, build.gradle, Cargo.toml, go.mod, pyproject.toml, package.json); marcar ou desmarcar uma pasta substitui a detecção. Pastas excluídas são ignoradas pela visualização do Projeto, Localizar em arquivos e pelo índice (reabra o projeto para reindexar).",
|
||||
"Folders are detected from build files (pom.xml, build.gradle, Cargo.toml, go.mod, pyproject.toml, package.json, composer.json); marking or unmarking a folder overrides detection. Excluded folders are skipped by the Project view, Find in Files and the index (reopen the project to re-index).": "As pastas são detectadas a partir de arquivos de build (pom.xml, build.gradle, Cargo.toml, go.mod, pyproject.toml, package.json, composer.json); marcar ou desmarcar uma pasta substitui a detecção. Pastas excluídas são ignoradas pela visualização do Projeto, Localizar em arquivos e pelo índice (reabra o projeto para reindexar).",
|
||||
"Project virtualenv (auto-detect)": "Virtualenv do projeto (detecção automática)",
|
||||
"<No interpreter>": "<Nenhum interpretador>",
|
||||
"Package name or requirement, e.g. requests or django==5.1": "Nome do pacote ou requisito, p. ex. requests ou django==5.1",
|
||||
|
||||
@@ -3873,7 +3873,7 @@
|
||||
"JDK Location": "JDK 位置",
|
||||
"Downloading JDK": "正在下载 JDK",
|
||||
"Use ; to separate name patterns, * for any number of symbols, ? for one.": "使用 ; 分隔名称模式,* 匹配任意数量的字符,? 匹配单个字符。",
|
||||
"Folders are detected from build files (pom.xml, build.gradle, Cargo.toml, go.mod, pyproject.toml, package.json); marking or unmarking a folder overrides detection. Excluded folders are skipped by the Project view, Find in Files and the index (reopen the project to re-index).": "文件夹根据构建文件(pom.xml、build.gradle、Cargo.toml、go.mod、pyproject.toml、package.json)检测;标记或取消标记文件夹会覆盖检测结果。项目视图、在文件中查找和索引会跳过已排除的文件夹(重新打开项目以重新索引)。",
|
||||
"Folders are detected from build files (pom.xml, build.gradle, Cargo.toml, go.mod, pyproject.toml, package.json, composer.json); marking or unmarking a folder overrides detection. Excluded folders are skipped by the Project view, Find in Files and the index (reopen the project to re-index).": "文件夹根据构建文件(pom.xml、build.gradle、Cargo.toml、go.mod、pyproject.toml、package.json、composer.json)检测;标记或取消标记文件夹会覆盖检测结果。项目视图、在文件中查找和索引会跳过已排除的文件夹(重新打开项目以重新索引)。",
|
||||
"Project virtualenv (auto-detect)": "项目 virtualenv(自动检测)",
|
||||
"<No interpreter>": "<无解释器>",
|
||||
"Package name or requirement, e.g. requests or django==5.1": "软件包名称或需求,例如 requests 或 django==5.1",
|
||||
|
||||
Reference in New Issue
Block a user