-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Manage when Loading versions...
is displayed and alert the user to when no versions are found
#80
base: develop
Are you sure you want to change the base?
Changes from 1 commit
66e9dd5
61e9ac5
b47cf85
c744f9a
2358cd7
9f6c455
21fdd34
759e28f
614952c
ca5921f
a2ddc27
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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<DependencyInfo> 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<PackageInRepo> groupAndDeps = collectGroupAndDeps(project, group, dependencyName); | ||
addToResults(sortedResultSet, groupAndDeps); | ||
List<PackageInRepo> 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<PackageInRepo> allPackages = RepositoryLoader.loadRepositories(project).stream() | ||
.map(url -> new PackageInRepo(dependencyInfo.group(), dependencyInfo.dependencyName(), url)) | ||
.collect(Collectors.toList()); | ||
addToResults(sortedResultSet, allPackages, dependencyInfo); | ||
} | ||
|
||
private List<PackageInRepo> collectGroupAndDeps( | ||
Project project, DependencyGroup group, DependencyName dependencyName) { | ||
private List<PackageInRepo> 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<PackageInRepo> groupAndDeps) { | ||
Map<DependencyVersion, AtomicInteger> 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> packageInRepo, DependencyInfo key) { | ||
|
||
Set<VersionResults> versionResults = | ||
packageInRepo.stream().map(versionExplorer::getVersions).collect(Collectors.toSet()); | ||
|
||
List<CompletableFuture<?>> pendingFutures = versionResults.stream() | ||
.filter(results -> results.versions().isEmpty()) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is actually super strange to me (I guess I'm still thinking in the mindset where you give a For each sealed interface VersionsResult permits AlreadyLoadedVersions, StillLoadingVersions {}
record AlreadyLoadedVersions(Set<DependencyVersion>) {}
record StillLoadingVersions(CompletableFuture<Set<DependencyVersion>>) {} There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think there's definitely a type somewhere here that takes a set/list of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So I have made quite a few changes, I've switched |
||
.map(VersionResults::future) | ||
.filter(future -> !future.isDone()) | ||
.collect(Collectors.toList()); | ||
|
||
if (!pendingFutures.isEmpty()) { | ||
CompletableFuture.anyOf(pendingFutures.toArray(new CompletableFuture[0])) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should trigger a refresh when any one of the futures completes which should schedule less refreshes than just doing a refresh for each future I think |
||
.thenAccept(completedFuture -> { | ||
CompletionRefreshUtil.triggerRefresh(); | ||
}); | ||
} | ||
|
||
Map<DependencyVersion, Integer> 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()) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. removes |
||
addAndRefresh(key); | ||
} | ||
|
||
Integer packageCount = packageInRepo.stream() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We actually want to count the number of unique artifact names and not the size of packageInRepo |
||
.map(PackageInRepo::dependencyName) | ||
.filter(Objects::nonNull) | ||
.distinct() | ||
.collect(Collectors.collectingAndThen(Collectors.counting(), Long::intValue)); | ||
|
||
List<LookupElement> 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()) { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need to handle these as a chunk so that we can be sure that there are no versions for all repos rather than just no versions for one repo