mirror of
https://github.com/l0ng-ai/openidea.git
synced 2026-11-14 16:07:52 +00:00
fix(libraries): keep External Libraries in a stable order and reload after a build sync (#3077)
External Libraries groups were listed in detection order, which differed between the streamed and final results; they are now sorted SDKs first, then libraries by name. A Maven sync that changed dependency versions without touching a manifest left old versions listed; every successful build sync now emits projectRootsChanged (Gradle already did) and External Libraries reloads on it.
This commit is contained in:
@@ -313,10 +313,11 @@ pub async fn build_sync(
|
||||
if tool == BuildTool::Gradle {
|
||||
// The sync may report project folders outside the project (`includeFlat`): index and list them.
|
||||
project.refresh_index_scope();
|
||||
project.sink.emit(ide_core::IdeEvent::ProjectRootsChanged { root: project.root.clone().into() });
|
||||
let p = project.clone();
|
||||
tokio::task::spawn_blocking(move || p.refresh_repositories());
|
||||
}
|
||||
// New source folders and dependency versions: the Project view and External Libraries reload.
|
||||
project.sink.emit(ide_core::IdeEvent::ProjectRootsChanged { root: project.root.clone().into() });
|
||||
if tool == BuildTool::Maven
|
||||
&& model.maven.is_some()
|
||||
&& let Some(bridge) = &project.kotlin_bridge
|
||||
|
||||
@@ -83,6 +83,14 @@ export function touchesManifest(paths: readonly string[]): boolean {
|
||||
);
|
||||
}
|
||||
|
||||
/** IDEA's order, whatever order the ecosystems were detected in: SDKs first, then libraries, each by name. */
|
||||
function sortedGroups(groups: readonly LibraryGroup[]): LibraryGroup[] {
|
||||
const rank = (g: LibraryGroup) => (SDK_KINDS.has(g.kind) ? 0 : 1);
|
||||
return [...groups].sort(
|
||||
(a, b) => rank(a) - rank(b) || a.name.localeCompare(b.name, undefined, { sensitivity: "base", numeric: true }),
|
||||
);
|
||||
}
|
||||
|
||||
/** The "External Libraries" node for `groups` (`< Python 3.12 (.venv) >` › `python3.12`, `site-packages`). */
|
||||
export function librariesRoot(groups: readonly LibraryGroup[]): ProjectViewRootContribution | null {
|
||||
if (!groups.length) return null;
|
||||
@@ -93,7 +101,7 @@ export function librariesRoot(groups: readonly LibraryGroup[]): ProjectViewRootC
|
||||
// After the project, before Scratches and Consoles (IDEA's order).
|
||||
order: 5,
|
||||
readOnly: true,
|
||||
children: groups.map((g) => ({
|
||||
children: sortedGroups(groups).map((g) => ({
|
||||
label: SDK_KINDS.has(g.kind) ? `< ${g.name} >` : g.name,
|
||||
icon: GROUP_ICONS[g.kind] ?? "library",
|
||||
children: g.roots.map((r) => ({
|
||||
@@ -192,12 +200,18 @@ export function activate(): () => void {
|
||||
clearTimeout(timer);
|
||||
timer = setTimeout(refresh, MANIFEST_REFRESH_DELAY);
|
||||
});
|
||||
// A Maven / Gradle sync changes the dependency versions without touching a manifest.
|
||||
const unsubRoots = onIdeEvent("projectRootsChanged", () => {
|
||||
clearTimeout(timer);
|
||||
timer = setTimeout(refresh, MANIFEST_REFRESH_DELAY);
|
||||
});
|
||||
return () => {
|
||||
seq++;
|
||||
if (request !== undefined) void commands.cancelRequest(request).catch(() => {});
|
||||
clearTimeout(timer);
|
||||
unsub();
|
||||
unsubFs();
|
||||
unsubRoots();
|
||||
disposeActions();
|
||||
clear();
|
||||
};
|
||||
|
||||
@@ -37,18 +37,46 @@ describe("External Libraries", () => {
|
||||
}),
|
||||
),
|
||||
)!;
|
||||
expect(root.children?.map((g) => g.icon)).toEqual([
|
||||
"file-java",
|
||||
"file-js",
|
||||
"file-npm",
|
||||
"file-rust",
|
||||
"file-cargo",
|
||||
"file-go",
|
||||
"file-gomod",
|
||||
"maven-project",
|
||||
"file-gradle",
|
||||
"library",
|
||||
expect(Object.fromEntries(root.children!.map((g) => [g.label, g.icon]))).toEqual({
|
||||
"< jdk >": "file-java",
|
||||
nodeCore: "file-js",
|
||||
nodeModules: "file-npm",
|
||||
"< rust >": "file-rust",
|
||||
cargo: "file-cargo",
|
||||
"< go >": "file-go",
|
||||
goModules: "file-gomod",
|
||||
maven: "maven-project",
|
||||
gradle: "file-gradle",
|
||||
other: "library",
|
||||
});
|
||||
});
|
||||
|
||||
it("lists SDKs first, then libraries by name, whatever order they were detected in", () => {
|
||||
const group = (name: string, kind: string) => ({ name, kind, roots: [{ name: "r", path: `/libs/${name}` }] });
|
||||
const detected = [
|
||||
group("Maven: org.junit:junit:4.13", "maven"),
|
||||
group("temurin-21", "jdk"),
|
||||
group("node_modules", "nodeModules"),
|
||||
group("Python 3.14", "python"),
|
||||
group("Cargo dependencies", "cargo"),
|
||||
group("Gradle: com.acme:lib:1.0", "gradle"),
|
||||
group("Go SDK 1.27.4", "go"),
|
||||
group("Maven: com.acme:core:2.1.27", "maven"),
|
||||
group("Node.js Core", "nodeCore"),
|
||||
];
|
||||
const labels = (groups: typeof detected) => librariesRoot(groups)!.children!.map((g) => g.label);
|
||||
expect(labels(detected)).toEqual([
|
||||
"< Go SDK 1.27.4 >",
|
||||
"< Python 3.14 >",
|
||||
"< temurin-21 >",
|
||||
"Cargo dependencies",
|
||||
"Gradle: com.acme:lib:1.0",
|
||||
"Maven: com.acme:core:2.1.27",
|
||||
"Maven: org.junit:junit:4.13",
|
||||
"node_modules",
|
||||
"Node.js Core",
|
||||
]);
|
||||
expect(labels([...detected].reverse())).toEqual(labels(detected));
|
||||
});
|
||||
|
||||
it("lists archives (src.zip, dependency jars) as browsable roots", () => {
|
||||
|
||||
Reference in New Issue
Block a user