From 66e9dd536388dda87f86e389ad2985efc1261b78 Mon Sep 17 00:00:00 2001 From: Finlay Williams Date: Thu, 14 Nov 2024 13:45:39 +0000 Subject: [PATCH 1/8] manage state --- .../intellij/CompletionRefreshUtil.java | 11 +- .../VersionCompletionContributor.java | 109 ++++++++++++------ .../versions/intellij/VersionExplorer.java | 24 ++-- 3 files changed, 86 insertions(+), 58 deletions(-) diff --git a/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/CompletionRefreshUtil.java b/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/CompletionRefreshUtil.java index 078aa81..a4f7de3 100644 --- a/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/CompletionRefreshUtil.java +++ b/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/CompletionRefreshUtil.java @@ -16,27 +16,18 @@ package com.palantir.gradle.versions.intellij; -import com.google.common.base.Suppliers; import com.intellij.codeInsight.completion.BaseCompletionService; import com.intellij.codeInsight.completion.CompletionProcess; import com.intellij.codeInsight.completion.CompletionProgressIndicator; import com.intellij.codeInsight.completion.CompletionService; import com.intellij.openapi.application.ApplicationManager; -import java.util.function.Supplier; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class CompletionRefreshUtil { private static final Logger log = LoggerFactory.getLogger(CompletionRefreshUtil.class); - public static Supplier refreshOnceSupplier() { - return Suppliers.memoize(() -> { - triggerRefresh(); - return null; - }); - } - - private static void triggerRefresh() { + public static void triggerRefresh() { ApplicationManager.getApplication().invokeLater(() -> { CompletionService completionService = CompletionService.getCompletionService(); if (completionService == null) { diff --git a/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/VersionCompletionContributor.java b/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/VersionCompletionContributor.java index 121b75b..42fcef8 100644 --- a/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/VersionCompletionContributor.java +++ b/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/VersionCompletionContributor.java @@ -15,6 +15,7 @@ */ package com.palantir.gradle.versions.intellij; +import com.google.common.collect.EvictingQueue; import com.intellij.codeInsight.completion.CompletionContributor; import com.intellij.codeInsight.completion.CompletionParameters; import com.intellij.codeInsight.completion.CompletionProvider; @@ -31,13 +32,17 @@ import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.ProcessingContext; import com.palantir.gradle.versions.intellij.VersionExplorer.PackageInRepo; +import com.palantir.gradle.versions.intellij.VersionExplorer.VersionResults; import com.palantir.gradle.versions.intellij.psi.VersionPropsDependencyVersion; import com.palantir.gradle.versions.intellij.psi.VersionPropsProperty; import com.palantir.gradle.versions.intellij.psi.VersionPropsTypes; import java.util.AbstractMap.SimpleEntry; import java.util.List; import java.util.Map; -import java.util.concurrent.atomic.AtomicInteger; +import java.util.Objects; +import java.util.Queue; +import java.util.Set; +import java.util.concurrent.CompletableFuture; import java.util.function.Function; import java.util.stream.Collectors; import one.util.streamex.StreamEx; @@ -47,6 +52,9 @@ public class VersionCompletionContributor extends CompletionContributor { GroupPartOrPackageNameExplorer.getInstance(); private final VersionExplorer versionExplorer = VersionExplorer.getInstance(); + private static final int MAX_SIZE = 100; + private final Queue loadedDependencies = EvictingQueue.create(MAX_SIZE); + VersionCompletionContributor() { extend( CompletionType.BASIC, @@ -55,24 +63,25 @@ public class VersionCompletionContributor extends CompletionContributor { @Override public void addCompletions( CompletionParameters parameters, ProcessingContext context, CompletionResultSet resultSet) { + DependencyInfo dependencyInfo = getDependencyInfo(parameters); - DependencyGroup group = dependencyInfo.group(); - DependencyName dependencyName = dependencyInfo.dependencyName(); Project project = parameters.getOriginalFile().getProject(); CompletionSorter sorter = CompletionSorter.emptySorter().weigh(new VersionWeigher()); CompletionResultSet sortedResultSet = resultSet.withRelevanceSorter(sorter); - addLoadingElement(sortedResultSet); + if (!loadedDependencies.contains(dependencyInfo)) { + addDisplayElement(sortedResultSet, "Loading Versions..."); + } - if (!dependencyName.name().contains("*")) { - handleDependencyWithoutWildcard(sortedResultSet, project, group, dependencyName); + if (!dependencyInfo.dependencyName().name().contains("*")) { + handleDependencyWithoutWildcard(sortedResultSet, project, dependencyInfo); return; } - List groupAndDeps = collectGroupAndDeps(project, group, dependencyName); - addToResults(sortedResultSet, groupAndDeps); + List packageInRepo = collectPackageInRepo(project, dependencyInfo); + addToResults(sortedResultSet, packageInRepo, dependencyInfo); } }); } @@ -91,9 +100,9 @@ private DependencyInfo getDependencyInfo(CompletionParameters parameters) { return new DependencyInfo(group, dependencyName); } - private void addLoadingElement(CompletionResultSet sortedResultSet) { + private void addDisplayElement(CompletionResultSet sortedResultSet, String elementText) { LookupElement loadingElement = PrioritizedLookupElement.withPriority( - LookupElementBuilder.create("Loading Versions...").withInsertHandler((elementContext, item) -> { + LookupElementBuilder.create(elementText).withInsertHandler((elementContext, item) -> { // Prevent insertion elementContext .getDocument() @@ -104,52 +113,82 @@ private void addLoadingElement(CompletionResultSet sortedResultSet) { } private void handleDependencyWithoutWildcard( - CompletionResultSet sortedResultSet, - Project project, - DependencyGroup group, - DependencyName dependencyName) { - RepositoryLoader.loadRepositories(project) - .forEach(url -> addToResults(sortedResultSet, List.of(new PackageInRepo(group, dependencyName, url)))); + CompletionResultSet sortedResultSet, Project project, DependencyInfo dependencyInfo) { + + List allPackages = RepositoryLoader.loadRepositories(project).stream() + .map(url -> new PackageInRepo(dependencyInfo.group(), dependencyInfo.dependencyName(), url)) + .collect(Collectors.toList()); + addToResults(sortedResultSet, allPackages, dependencyInfo); } - private List collectGroupAndDeps( - Project project, DependencyGroup group, DependencyName dependencyName) { + private List collectPackageInRepo(Project project, DependencyInfo dependencyInfo) { - String dependencyNamePrefix = dependencyName.name().replace("*", ""); + String dependencyNamePrefix = dependencyInfo.dependencyName().name().replace("*", ""); return StreamEx.of(RepositoryLoader.loadRepositories(project)) - .flatMap(url -> StreamEx.of( - groupPartOrPackageNameExplorer.getCancelableGroupPartOrPackageName(group, url)) + .flatMap(url -> StreamEx.of(groupPartOrPackageNameExplorer.getCancelableGroupPartOrPackageName( + dependencyInfo.group(), url)) .filter(pkgName -> pkgName.name().startsWith(dependencyNamePrefix)) .map(pkgName -> new SimpleEntry<>(url, pkgName))) .map(entry -> { RepositoryUrl url = entry.getKey(); GroupPartOrPackageName pkgName = entry.getValue(); DependencyName depName = DependencyName.of(pkgName.name()); - return new PackageInRepo(group, depName, url); + return new PackageInRepo(dependencyInfo.group(), depName, url); }) .toList(); } - private void addToResults(CompletionResultSet resultSet, List groupAndDeps) { - Map versionCounts = StreamEx.of(groupAndDeps) - .flatMap(groupAndDep -> - versionExplorer - .getVersions(groupAndDep, CompletionRefreshUtil.refreshOnceSupplier()::get) - .stream()) - .collect(Collectors.toConcurrentMap( - Function.identity(), v -> new AtomicInteger(1), (existingCount, newCount) -> { - existingCount.addAndGet(newCount.get()); - return existingCount; - })); + private void addToResults(CompletionResultSet resultSet, List packageInRepo, DependencyInfo key) { + + Set versionResults = + packageInRepo.stream().map(versionExplorer::getVersions).collect(Collectors.toSet()); + + List> pendingFutures = versionResults.stream() + .filter(results -> results.versions().isEmpty()) + .map(VersionResults::future) + .filter(future -> !future.isDone()) + .collect(Collectors.toList()); + + if (!pendingFutures.isEmpty()) { + CompletableFuture.anyOf(pendingFutures.toArray(new CompletableFuture[0])) + .thenAccept(completedFuture -> { + CompletionRefreshUtil.triggerRefresh(); + }); + } + + Map versionCounts = StreamEx.of(versionResults) + .flatMap(result -> result.versions().stream()) + .collect(Collectors.toMap(Function.identity(), v -> 1, Integer::sum)); + + if (pendingFutures.isEmpty() && versionCounts.isEmpty()) { + addDisplayElement(resultSet, "No versions found"); + addAndRefresh(key); + } + + if (!versionCounts.isEmpty()) { + addAndRefresh(key); + } + + Integer packageCount = packageInRepo.stream() + .map(PackageInRepo::dependencyName) + .filter(Objects::nonNull) + .distinct() + .collect(Collectors.collectingAndThen(Collectors.counting(), Long::intValue)); List lookupElements = versionCounts.entrySet().stream() - .map(entry -> - createLookupElement(entry.getKey(), entry.getValue().get(), groupAndDeps.size())) + .map(entry -> createLookupElement(entry.getKey(), entry.getValue(), packageCount)) .collect(Collectors.toList()); resultSet.addAllElements(lookupElements); } + private void addAndRefresh(DependencyInfo key) { + if (!loadedDependencies.contains(key)) { + CompletionRefreshUtil.triggerRefresh(); + loadedDependencies.add(key); + } + } + private LookupElement createLookupElement(DependencyVersion version, int count, int total) { String typeText = ((total > 1) ? count + "/" + total + " packages" : ""); if (version.isLatest()) { diff --git a/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/VersionExplorer.java b/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/VersionExplorer.java index 9fa3181..cf46d28 100644 --- a/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/VersionExplorer.java +++ b/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/VersionExplorer.java @@ -32,6 +32,7 @@ import java.util.Locale; import java.util.Optional; import java.util.Set; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; import java.util.regex.Pattern; import java.util.stream.Collectors; @@ -54,23 +55,20 @@ public final class VersionExplorer { .maximumSize(10000) .buildAsync(this::fetchAndParseFromUrl); - public record PackageInRepo(DependencyGroup group, DependencyName dependencyPackage, RepositoryUrl repositoryUrl) {} + public record PackageInRepo(DependencyGroup group, DependencyName dependencyName, RepositoryUrl repositoryUrl) {} - public Set getVersions(PackageInRepo groupAndDep, Runnable onLoadMore) { - String urlString = urlFor(groupAndDep); + public record VersionResults(Set versions, CompletableFuture> future) {} + + public VersionResults getVersions(PackageInRepo packageInRepo) { + String urlString = urlFor(packageInRepo); Optional> cachedVersions = Optional.ofNullable(shortLivedVersionCache.synchronous().getIfPresent(urlString)); - if (cachedVersions.isPresent()) { - return cachedVersions.get(); - } - - shortLivedVersionCache.get(urlString).thenAccept(result -> { - onLoadMore.run(); - }); - - return Collections.emptySet(); + return cachedVersions + .map(dependencyVersions -> + new VersionResults(dependencyVersions, CompletableFuture.completedFuture(dependencyVersions))) + .orElseGet(() -> new VersionResults(Collections.emptySet(), shortLivedVersionCache.get(urlString))); } private Set fetchAndParseFromUrl(String urlString) { @@ -91,7 +89,7 @@ private Set fetchAndParseFromUrl(String urlString) { private static @NotNull String urlFor(PackageInRepo groupAndDep) { return groupAndDep.repositoryUrl().url() + groupAndDep.group().asUrlString() - + groupAndDep.dependencyPackage().name() + "/maven-metadata.xml"; + + groupAndDep.dependencyName().name() + "/maven-metadata.xml"; } private Set parseVersionsFromContent(String content) { From 61e9ac56cf4edff6408d9b591306b0adfd6c1410 Mon Sep 17 00:00:00 2001 From: svc-changelog Date: Thu, 14 Nov 2024 13:53:25 +0000 Subject: [PATCH 2/8] Add generated changelog entries --- changelog/@unreleased/pr-80.v2.yml | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 changelog/@unreleased/pr-80.v2.yml diff --git a/changelog/@unreleased/pr-80.v2.yml b/changelog/@unreleased/pr-80.v2.yml new file mode 100644 index 0000000..461bbd7 --- /dev/null +++ b/changelog/@unreleased/pr-80.v2.yml @@ -0,0 +1,6 @@ +type: fix +fix: + description: Manage when `Loading versions...` is displayed and alert the user to + when no versions are found + links: + - https://github.com/palantir/gradle-consistent-versions-idea-plugin/pull/80 From b47cf85abcbb9e709bf4467aab9d1a52e940ac0d Mon Sep 17 00:00:00 2001 From: Finlay Williams Date: Thu, 14 Nov 2024 14:03:25 +0000 Subject: [PATCH 3/8] schedule not tigger --- .../gradle/versions/intellij/CompletionRefreshUtil.java | 2 +- .../versions/intellij/VersionCompletionContributor.java | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/CompletionRefreshUtil.java b/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/CompletionRefreshUtil.java index a4f7de3..980cb49 100644 --- a/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/CompletionRefreshUtil.java +++ b/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/CompletionRefreshUtil.java @@ -27,7 +27,7 @@ public class CompletionRefreshUtil { private static final Logger log = LoggerFactory.getLogger(CompletionRefreshUtil.class); - public static void triggerRefresh() { + public static void scheduleRefresh() { ApplicationManager.getApplication().invokeLater(() -> { CompletionService completionService = CompletionService.getCompletionService(); if (completionService == null) { diff --git a/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/VersionCompletionContributor.java b/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/VersionCompletionContributor.java index 42fcef8..f3b9f49 100644 --- a/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/VersionCompletionContributor.java +++ b/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/VersionCompletionContributor.java @@ -52,8 +52,7 @@ public class VersionCompletionContributor extends CompletionContributor { GroupPartOrPackageNameExplorer.getInstance(); private final VersionExplorer versionExplorer = VersionExplorer.getInstance(); - private static final int MAX_SIZE = 100; - private final Queue loadedDependencies = EvictingQueue.create(MAX_SIZE); + private final Queue loadedDependencies = EvictingQueue.create(100); VersionCompletionContributor() { extend( @@ -152,7 +151,7 @@ private void addToResults(CompletionResultSet resultSet, List pac if (!pendingFutures.isEmpty()) { CompletableFuture.anyOf(pendingFutures.toArray(new CompletableFuture[0])) .thenAccept(completedFuture -> { - CompletionRefreshUtil.triggerRefresh(); + CompletionRefreshUtil.scheduleRefresh(); }); } @@ -184,7 +183,7 @@ private void addToResults(CompletionResultSet resultSet, List pac private void addAndRefresh(DependencyInfo key) { if (!loadedDependencies.contains(key)) { - CompletionRefreshUtil.triggerRefresh(); + CompletionRefreshUtil.scheduleRefresh(); loadedDependencies.add(key); } } From 9f6c45544780e8860a189f06ec28320d2ecc2562 Mon Sep 17 00:00:00 2001 From: Finlay Williams Date: Fri, 15 Nov 2024 10:35:42 +0000 Subject: [PATCH 4/8] use aggeragte class --- .../VersionCompletionContributor.java | 34 +++------- .../versions/intellij/VersionExplorer.java | 32 ++++++--- .../intellij/VersionsResultAggregate.java | 67 +++++++++++++++++++ 3 files changed, 97 insertions(+), 36 deletions(-) create mode 100644 gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/VersionsResultAggregate.java diff --git a/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/VersionCompletionContributor.java b/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/VersionCompletionContributor.java index f3b9f49..b8fb9e5 100644 --- a/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/VersionCompletionContributor.java +++ b/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/VersionCompletionContributor.java @@ -32,7 +32,6 @@ import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.ProcessingContext; import com.palantir.gradle.versions.intellij.VersionExplorer.PackageInRepo; -import com.palantir.gradle.versions.intellij.VersionExplorer.VersionResults; import com.palantir.gradle.versions.intellij.psi.VersionPropsDependencyVersion; import com.palantir.gradle.versions.intellij.psi.VersionPropsProperty; import com.palantir.gradle.versions.intellij.psi.VersionPropsTypes; @@ -41,9 +40,6 @@ import java.util.Map; import java.util.Objects; import java.util.Queue; -import java.util.Set; -import java.util.concurrent.CompletableFuture; -import java.util.function.Function; import java.util.stream.Collectors; import one.util.streamex.StreamEx; @@ -138,28 +134,13 @@ private List collectPackageInRepo(Project project, DependencyInfo } private void addToResults(CompletionResultSet resultSet, List packageInRepo, DependencyInfo key) { + VersionsResultAggregate results = versionExplorer.getVersions(packageInRepo); - Set versionResults = - packageInRepo.stream().map(versionExplorer::getVersions).collect(Collectors.toSet()); + results.scheduleRefreshOnCompletion(CompletionRefreshUtil::scheduleRefresh); - List> pendingFutures = versionResults.stream() - .filter(results -> results.versions().isEmpty()) - .map(VersionResults::future) - .filter(future -> !future.isDone()) - .collect(Collectors.toList()); - - if (!pendingFutures.isEmpty()) { - CompletableFuture.anyOf(pendingFutures.toArray(new CompletableFuture[0])) - .thenAccept(completedFuture -> { - CompletionRefreshUtil.scheduleRefresh(); - }); - } + Map versionCounts = results.getVersionCounts(); - Map versionCounts = StreamEx.of(versionResults) - .flatMap(result -> result.versions().stream()) - .collect(Collectors.toMap(Function.identity(), v -> 1, Integer::sum)); - - if (pendingFutures.isEmpty() && versionCounts.isEmpty()) { + if (results.hasNoVersions()) { addDisplayElement(resultSet, "No versions found"); addAndRefresh(key); } @@ -168,11 +149,11 @@ private void addToResults(CompletionResultSet resultSet, List pac addAndRefresh(key); } - Integer packageCount = packageInRepo.stream() + long packageCount = packageInRepo.stream() .map(PackageInRepo::dependencyName) .filter(Objects::nonNull) .distinct() - .collect(Collectors.collectingAndThen(Collectors.counting(), Long::intValue)); + .count(); List lookupElements = versionCounts.entrySet().stream() .map(entry -> createLookupElement(entry.getKey(), entry.getValue(), packageCount)) @@ -181,6 +162,7 @@ private void addToResults(CompletionResultSet resultSet, List pac resultSet.addAllElements(lookupElements); } + private void addAndRefresh(DependencyInfo key) { if (!loadedDependencies.contains(key)) { CompletionRefreshUtil.scheduleRefresh(); @@ -188,7 +170,7 @@ private void addAndRefresh(DependencyInfo key) { } } - private LookupElement createLookupElement(DependencyVersion version, int count, int total) { + private LookupElement createLookupElement(DependencyVersion version, Long count, Long total) { String typeText = ((total > 1) ? count + "/" + total + " packages" : ""); if (version.isLatest()) { typeText = ((total > 1) ? "latest for " : "latest") + typeText; diff --git a/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/VersionExplorer.java b/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/VersionExplorer.java index cf46d28..240fbd8 100644 --- a/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/VersionExplorer.java +++ b/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/VersionExplorer.java @@ -55,20 +55,24 @@ public final class VersionExplorer { .maximumSize(10000) .buildAsync(this::fetchAndParseFromUrl); - public record PackageInRepo(DependencyGroup group, DependencyName dependencyName, RepositoryUrl repositoryUrl) {} - - public record VersionResults(Set versions, CompletableFuture> future) {} + public VersionsResultAggregate getVersions(List packagesInRepo) { + List versionsResults = + packagesInRepo.stream().map(this::getVersions).collect(Collectors.toList()); + return VersionsResultAggregate.of(versionsResults); + } - public VersionResults getVersions(PackageInRepo packageInRepo) { + public VersionsResult getVersions(PackageInRepo packageInRepo) { String urlString = urlFor(packageInRepo); - Optional> cachedVersions = - Optional.ofNullable(shortLivedVersionCache.synchronous().getIfPresent(urlString)); + Set cachedVersions = + shortLivedVersionCache.synchronous().getIfPresent(urlString); - return cachedVersions - .map(dependencyVersions -> - new VersionResults(dependencyVersions, CompletableFuture.completedFuture(dependencyVersions))) - .orElseGet(() -> new VersionResults(Collections.emptySet(), shortLivedVersionCache.get(urlString))); + if (cachedVersions != null) { + return new AlreadyLoadedVersions(cachedVersions); + } else { + CompletableFuture> future = shortLivedVersionCache.get(urlString); + return new StillLoadingVersions(future); + } } private Set fetchAndParseFromUrl(String urlString) { @@ -142,4 +146,12 @@ static VersionExplorer getInstance() { } private VersionExplorer() {} + + public record PackageInRepo(DependencyGroup group, DependencyName dependencyName, RepositoryUrl repositoryUrl) {} + + public sealed interface VersionsResult permits AlreadyLoadedVersions, StillLoadingVersions {} + + record AlreadyLoadedVersions(Set versions) implements VersionsResult {} + + record StillLoadingVersions(CompletableFuture> future) implements VersionsResult {} } diff --git a/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/VersionsResultAggregate.java b/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/VersionsResultAggregate.java new file mode 100644 index 0000000..d9c7009 --- /dev/null +++ b/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/VersionsResultAggregate.java @@ -0,0 +1,67 @@ +/* + * (c) Copyright 2024 Palantir Technologies Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.palantir.gradle.versions.intellij; + +import com.palantir.gradle.versions.intellij.VersionExplorer.AlreadyLoadedVersions; +import com.palantir.gradle.versions.intellij.VersionExplorer.StillLoadingVersions; +import com.palantir.gradle.versions.intellij.VersionExplorer.VersionsResult; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; +import org.immutables.value.Value; + +@Value.Immutable +public abstract class VersionsResultAggregate { + + public abstract List results(); + + public final boolean isAllComplete() { + return getIncompleteFutures().isEmpty(); + } + + public final boolean hasNoVersions() { + return isAllComplete() && getVersionCounts().isEmpty(); + } + + public final Map getVersionCounts() { + return results().stream() + .filter(result -> result instanceof AlreadyLoadedVersions) + .flatMap(av -> ((AlreadyLoadedVersions) av).versions().stream()) + .collect(Collectors.groupingBy(v -> v, Collectors.counting())); + } + + public final void scheduleRefreshOnCompletion(Runnable refreshAction) { + List> pendingFutures = getIncompleteFutures(); + if (!pendingFutures.isEmpty()) { + CompletableFuture.anyOf(pendingFutures.toArray(new CompletableFuture[0])) + .thenRun(refreshAction); + } + } + + private List> getIncompleteFutures() { + return results().stream() + .filter(result -> result instanceof StillLoadingVersions) + .map(sv -> ((StillLoadingVersions) sv).future()) + .filter(future -> !future.isDone()) + .collect(Collectors.toList()); + } + + public static VersionsResultAggregate of(List results) { + return ImmutableVersionsResultAggregate.builder().results(results).build(); + } +} \ No newline at end of file From 21fdd341e6f5b2915b189ac9b2f0bf48ea33d806 Mon Sep 17 00:00:00 2001 From: Finlay Williams Date: Fri, 15 Nov 2024 11:14:36 +0000 Subject: [PATCH 5/8] much tidyer --- .../VersionCompletionContributor.java | 5 +-- .../versions/intellij/VersionExplorer.java | 42 +++++++++---------- ...ultAggregate.java => VersionsResults.java} | 36 ++++++++-------- 3 files changed, 42 insertions(+), 41 deletions(-) rename gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/{VersionsResultAggregate.java => VersionsResults.java} (59%) diff --git a/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/VersionCompletionContributor.java b/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/VersionCompletionContributor.java index b8fb9e5..49e43a7 100644 --- a/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/VersionCompletionContributor.java +++ b/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/VersionCompletionContributor.java @@ -134,9 +134,9 @@ private List collectPackageInRepo(Project project, DependencyInfo } private void addToResults(CompletionResultSet resultSet, List packageInRepo, DependencyInfo key) { - VersionsResultAggregate results = versionExplorer.getVersions(packageInRepo); + VersionsResults results = versionExplorer.getVersions(packageInRepo); - results.scheduleRefreshOnCompletion(CompletionRefreshUtil::scheduleRefresh); + results.scheduleRunnableOnCompletion(CompletionRefreshUtil::scheduleRefresh); Map versionCounts = results.getVersionCounts(); @@ -162,7 +162,6 @@ private void addToResults(CompletionResultSet resultSet, List pac resultSet.addAllElements(lookupElements); } - private void addAndRefresh(DependencyInfo key) { if (!loadedDependencies.contains(key)) { CompletionRefreshUtil.scheduleRefresh(); diff --git a/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/VersionExplorer.java b/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/VersionExplorer.java index 240fbd8..2785dde 100644 --- a/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/VersionExplorer.java +++ b/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/VersionExplorer.java @@ -30,12 +30,15 @@ import java.util.LinkedHashSet; import java.util.List; import java.util.Locale; +import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; import java.util.regex.Pattern; import java.util.stream.Collectors; +import one.util.streamex.EntryStream; +import one.util.streamex.StreamEx; import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -55,24 +58,27 @@ public final class VersionExplorer { .maximumSize(10000) .buildAsync(this::fetchAndParseFromUrl); - public VersionsResultAggregate getVersions(List packagesInRepo) { - List versionsResults = - packagesInRepo.stream().map(this::getVersions).collect(Collectors.toList()); - return VersionsResultAggregate.of(versionsResults); - } + public VersionsResults getVersions(List packagesInRepo) { - public VersionsResult getVersions(PackageInRepo packageInRepo) { - String urlString = urlFor(packageInRepo); + Map>> alreadyInCacheOrNot = StreamEx.of(packagesInRepo) + .toMap(packageInRepo -> { + String urlString = urlFor(packageInRepo); + return Optional.ofNullable( + shortLivedVersionCache.synchronous().getIfPresent(urlString)); + }); - Set cachedVersions = - shortLivedVersionCache.synchronous().getIfPresent(urlString); + List>> futures = EntryStream.of(alreadyInCacheOrNot) + .filterValues(Optional::isEmpty) + .mapKeyValue((packageInRepo, emptyOpt) -> shortLivedVersionCache.get(urlFor(packageInRepo))) + .toList(); - if (cachedVersions != null) { - return new AlreadyLoadedVersions(cachedVersions); - } else { - CompletableFuture> future = shortLivedVersionCache.get(urlString); - return new StillLoadingVersions(future); - } + List> versions = EntryStream.of(alreadyInCacheOrNot) + .values() + .filter(Optional::isPresent) + .flatMap(Optional::stream) + .toList(); + + return VersionsResults.of(versions, futures); } private Set fetchAndParseFromUrl(String urlString) { @@ -148,10 +154,4 @@ static VersionExplorer getInstance() { private VersionExplorer() {} public record PackageInRepo(DependencyGroup group, DependencyName dependencyName, RepositoryUrl repositoryUrl) {} - - public sealed interface VersionsResult permits AlreadyLoadedVersions, StillLoadingVersions {} - - record AlreadyLoadedVersions(Set versions) implements VersionsResult {} - - record StillLoadingVersions(CompletableFuture> future) implements VersionsResult {} } diff --git a/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/VersionsResultAggregate.java b/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/VersionsResults.java similarity index 59% rename from gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/VersionsResultAggregate.java rename to gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/VersionsResults.java index d9c7009..3b77433 100644 --- a/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/VersionsResultAggregate.java +++ b/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/VersionsResults.java @@ -16,19 +16,20 @@ package com.palantir.gradle.versions.intellij; -import com.palantir.gradle.versions.intellij.VersionExplorer.AlreadyLoadedVersions; -import com.palantir.gradle.versions.intellij.VersionExplorer.StillLoadingVersions; -import com.palantir.gradle.versions.intellij.VersionExplorer.VersionsResult; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.concurrent.CompletableFuture; +import java.util.function.Function; import java.util.stream.Collectors; import org.immutables.value.Value; @Value.Immutable -public abstract class VersionsResultAggregate { +public abstract class VersionsResults { - public abstract List results(); + public abstract List> alreadyLoadedVersions(); + + public abstract List>> stillLoadingVersions(); public final boolean isAllComplete() { return getIncompleteFutures().isEmpty(); @@ -39,29 +40,30 @@ public final boolean hasNoVersions() { } public final Map getVersionCounts() { - return results().stream() - .filter(result -> result instanceof AlreadyLoadedVersions) - .flatMap(av -> ((AlreadyLoadedVersions) av).versions().stream()) - .collect(Collectors.groupingBy(v -> v, Collectors.counting())); + return alreadyLoadedVersions().stream() + .collect(Collectors.flatMapping( + Set::stream, Collectors.groupingBy(Function.identity(), Collectors.counting()))); } - public final void scheduleRefreshOnCompletion(Runnable refreshAction) { + public final void scheduleRunnableOnCompletion(Runnable runnable) { List> pendingFutures = getIncompleteFutures(); if (!pendingFutures.isEmpty()) { CompletableFuture.anyOf(pendingFutures.toArray(new CompletableFuture[0])) - .thenRun(refreshAction); + .thenRun(runnable); } } private List> getIncompleteFutures() { - return results().stream() - .filter(result -> result instanceof StillLoadingVersions) - .map(sv -> ((StillLoadingVersions) sv).future()) + return stillLoadingVersions().stream() .filter(future -> !future.isDone()) .collect(Collectors.toList()); } - public static VersionsResultAggregate of(List results) { - return ImmutableVersionsResultAggregate.builder().results(results).build(); + public static VersionsResults of( + List> alreadyLoaded, List>> stillLoading) { + return ImmutableVersionsResults.builder() + .alreadyLoadedVersions(alreadyLoaded) + .stillLoadingVersions(stillLoading) + .build(); } -} \ No newline at end of file +} From 759e28fba0863d87697c0f747cb1998ba01f1826 Mon Sep 17 00:00:00 2001 From: Finlay Williams Date: Fri, 15 Nov 2024 11:19:42 +0000 Subject: [PATCH 6/8] hasSomeVersions --- .../versions/intellij/VersionCompletionContributor.java | 2 +- .../palantir/gradle/versions/intellij/VersionsResults.java | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/VersionCompletionContributor.java b/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/VersionCompletionContributor.java index 49e43a7..aae10c6 100644 --- a/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/VersionCompletionContributor.java +++ b/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/VersionCompletionContributor.java @@ -145,7 +145,7 @@ private void addToResults(CompletionResultSet resultSet, List pac addAndRefresh(key); } - if (!versionCounts.isEmpty()) { + if (results.hasSomeVersions()) { addAndRefresh(key); } diff --git a/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/VersionsResults.java b/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/VersionsResults.java index 3b77433..6b93f06 100644 --- a/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/VersionsResults.java +++ b/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/VersionsResults.java @@ -35,8 +35,12 @@ public final boolean isAllComplete() { return getIncompleteFutures().isEmpty(); } + public final boolean hasSomeVersions() { + return !getVersionCounts().isEmpty(); + } + public final boolean hasNoVersions() { - return isAllComplete() && getVersionCounts().isEmpty(); + return isAllComplete() && !hasSomeVersions(); } public final Map getVersionCounts() { From 614952c3ff930473ba001465340da8ed58648cd7 Mon Sep 17 00:00:00 2001 From: Finlay Williams Date: Fri, 15 Nov 2024 11:21:05 +0000 Subject: [PATCH 7/8] re-order function --- .../versions/intellij/VersionCompletionContributor.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/VersionCompletionContributor.java b/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/VersionCompletionContributor.java index aae10c6..457e49f 100644 --- a/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/VersionCompletionContributor.java +++ b/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/VersionCompletionContributor.java @@ -138,8 +138,6 @@ private void addToResults(CompletionResultSet resultSet, List pac results.scheduleRunnableOnCompletion(CompletionRefreshUtil::scheduleRefresh); - Map versionCounts = results.getVersionCounts(); - if (results.hasNoVersions()) { addDisplayElement(resultSet, "No versions found"); addAndRefresh(key); @@ -155,6 +153,8 @@ private void addToResults(CompletionResultSet resultSet, List pac .distinct() .count(); + Map versionCounts = results.getVersionCounts(); + List lookupElements = versionCounts.entrySet().stream() .map(entry -> createLookupElement(entry.getKey(), entry.getValue(), packageCount)) .collect(Collectors.toList()); From ca5921f8975e672764100cab8ce83b067eb30eb3 Mon Sep 17 00:00:00 2001 From: Finlay Williams Date: Fri, 15 Nov 2024 11:24:31 +0000 Subject: [PATCH 8/8] debug log --- .../versions/intellij/VersionCompletionContributor.java | 6 ++++++ .../gradle/versions/intellij/VersionsResults.java | 8 ++++++++ 2 files changed, 14 insertions(+) diff --git a/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/VersionCompletionContributor.java b/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/VersionCompletionContributor.java index 457e49f..7f22471 100644 --- a/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/VersionCompletionContributor.java +++ b/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/VersionCompletionContributor.java @@ -42,8 +42,12 @@ import java.util.Queue; import java.util.stream.Collectors; import one.util.streamex.StreamEx; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class VersionCompletionContributor extends CompletionContributor { + private static final Logger log = LoggerFactory.getLogger(VersionCompletionContributor.class); + private final GroupPartOrPackageNameExplorer groupPartOrPackageNameExplorer = GroupPartOrPackageNameExplorer.getInstance(); private final VersionExplorer versionExplorer = VersionExplorer.getInstance(); @@ -136,6 +140,8 @@ private List collectPackageInRepo(Project project, DependencyInfo private void addToResults(CompletionResultSet resultSet, List packageInRepo, DependencyInfo key) { VersionsResults results = versionExplorer.getVersions(packageInRepo); + log.debug("{} of {} futures pending", results.pendingCount(), results.computedCount() + results.pendingCount()); + results.scheduleRunnableOnCompletion(CompletionRefreshUtil::scheduleRefresh); if (results.hasNoVersions()) { diff --git a/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/VersionsResults.java b/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/VersionsResults.java index 6b93f06..f5d2d5f 100644 --- a/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/VersionsResults.java +++ b/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/VersionsResults.java @@ -43,6 +43,14 @@ public final boolean hasNoVersions() { return isAllComplete() && !hasSomeVersions(); } + public final long computedCount() { + return alreadyLoadedVersions().size(); + } + + public final long pendingCount() { + return stillLoadingVersions().size(); + } + public final Map getVersionCounts() { return alreadyLoadedVersions().stream() .collect(Collectors.flatMapping(