Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
9512b56
style: spotless reformat, no functional change
itsaky-adfa Sep 18, 2026
13ba784
ADFA-6228: Let an index query be scoped to a set of sources
itsaky-adfa Sep 18, 2026
7c14264
style: spotless reformat, no functional change
itsaky-adfa Sep 18, 2026
e1d0678
ADFA-6228: Apply the active-source filter before the limit, not after it
itsaky-adfa Sep 18, 2026
39d2563
ADFA-6228: Project a single indexed column, filtered by a query
itsaky-adfa Sep 18, 2026
bcee666
ADFA-6228: Make prefix queries use the column index and stop treating…
itsaky-adfa Sep 18, 2026
ec8c2a7
style: spotless reformat, no functional change
itsaky-adfa Sep 18, 2026
268f352
style: spotless reformat, no functional change
itsaky-adfa Sep 23, 2026
63e0b1d
ADFA-6228: Index facades, package-private classes, and Kotlin nested …
itsaky-adfa Sep 23, 2026
85c42ef
ADFA-6228: Stop indexing anonymous classes and multi-release module-i…
itsaky-adfa Sep 23, 2026
9ef4088
ADFA-6228: Cap unimported-symbol completion by items delivered, not r…
itsaky-adfa Sep 18, 2026
7963cae
ADFA-6228: Filter by kind in the query, so the unbounded package look…
itsaky-adfa Sep 18, 2026
80f867a
style: spotless reformat, no functional change
itsaky-adfa Sep 23, 2026
187a7c6
ADFA-6228: Identify index rows by source and key, and resolve shared …
itsaky-adfa Sep 23, 2026
30fb0c9
ADFA-6228: Version the index format and rebuild on any mismatch
itsaky-adfa Sep 23, 2026
fd48adf
ADFA-6228: Enable WAL on the SQLite index, which androidx leaves off …
itsaky-adfa Sep 23, 2026
e570ffd
ADFA-6228: Re-index a library JAR whose size or modification time cha…
itsaky-adfa Sep 23, 2026
1188ce0
ADFA-6228: Explain index queries through the SQL the index itself builds
itsaky-adfa Sep 23, 2026
39787d8
ADFA-6228: Keep scope and kind terms out of index selection when a qu…
itsaky-adfa Sep 23, 2026
6ce2c9d
style: spotless reformat, no functional change
itsaky-adfa Sep 23, 2026
be1e382
ADFA-6228: Collect query planner statistics once a bulk indexing pass…
itsaky-adfa Sep 23, 2026
031ac27
ADFA-6228: Copy IndexQueryBuilder.anyOf's collection instead of keepi…
itsaky-adfa Sep 23, 2026
3eb4129
ADFA-6228: Make FilteredIndex.activeSources() final and trim its stal…
itsaky-adfa Sep 23, 2026
f580753
ADFA-6228: Document distinctValues' limit as capping rather than only…
itsaky-adfa Sep 23, 2026
013cad8
ADFA-6228: Fix chunked query correctness and the prefix range's upper…
itsaky-adfa Sep 23, 2026
aa37db7
ADFA-6228: Serialise re-indexing of the same source
itsaky-adfa Sep 23, 2026
a15202a
ADFA-6228: Check for cancellation before each insert batch
itsaky-adfa Sep 23, 2026
5b9929f
ADFA-6228: Fingerprint generated JARs instead of caching by path alone
itsaky-adfa Sep 23, 2026
5ddefc7
style: spotless reformat, no functional change
itsaky-adfa Sep 23, 2026
9ae7ec0
ADFA-6228: Open each cached JAR file system once under contention
itsaky-adfa Sep 23, 2026
f8e08be
ADFA-6228: Add a regression test for concurrent createFs
itsaky-adfa Sep 23, 2026
c70b364
ADFA-6228: Delete the unused per-scanner JAR entry points
itsaky-adfa Sep 23, 2026
fdedc44
ADFA-6228: Take nested class visibility and staticness from InnerClasses
itsaky-adfa Sep 23, 2026
54b50ce
ADFA-6228: Skip META-INF entries so multi-release copies cannot overw…
itsaky-adfa Sep 23, 2026
619880a
ADFA-6228: Name Kotlin classes by their class file internal name
itsaky-adfa Sep 23, 2026
098bcaf
ADFA-6228: Offer no file facades or companion objects as unimported c…
itsaky-adfa Sep 23, 2026
19c32f3
ADFA-6228: Fail rather than skip the differential test, and add a Jav…
itsaky-adfa Sep 23, 2026
d54f5b0
ADFA-6228: Query exact unimported-symbol matches before prefix matches
itsaky-adfa Sep 23, 2026
7a9c757
ADFA-6228: Deduplicate AddImportAction candidates by qualified name
itsaky-adfa Sep 23, 2026
4691308
ADFA-6228: Tighten the scanner and completion candidate tests
itsaky-adfa Sep 23, 2026
44d6ef0
ADFA-6228: Convert pre-existing multi-line comments to block form and…
itsaky-adfa Sep 23, 2026
7ae0970
ADFA-6228: Deduplicate Kotlin import candidates after the visibility …
itsaky-adfa Sep 25, 2026
f168e1b
ADFA-6228: Read newer Kotlin metadata leniently instead of dropping t…
itsaky-adfa Sep 25, 2026
4aa024d
ADFA-6228: Key Kotlin extension callables by their receiver too
itsaky-adfa Oct 7, 2026
a1286b4
ADFA-6228: Import an unimported type alias chosen from Kotlin completion
itsaky-adfa Oct 7, 2026
bf346be
ADFA-6228: Leave member callables out of the unimported-symbol queries
itsaky-adfa Oct 7, 2026
973203f
ADFA-6228: Decide JVM class nesting from InnerClasses, not a '$' in t…
itsaky-adfa Oct 8, 2026
c72fa57
ADFA-6228: Reject an index query on a field the descriptor does not d…
itsaky-adfa Oct 8, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package org.appdevforall.codeonthego.indexing

import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.appdevforall.codeonthego.indexing.api.IndexQuery
import org.appdevforall.codeonthego.indexing.api.Indexable
import org.appdevforall.codeonthego.indexing.api.ReadableIndex
Expand All @@ -19,8 +21,8 @@ import java.util.concurrent.ConcurrentHashMap
*/
open class FilteredIndex<T : Indexable>(
private val backing: ReadableIndex<T>,
) : ReadableIndex<T>, Closeable {

) : ReadableIndex<T>,
Closeable {
/**
* The set of source IDs whose entries are visible.
* Uses a concurrent set for thread-safe reads during queries.
Expand Down Expand Up @@ -57,50 +59,70 @@ open class FilteredIndex<T : Indexable>(
/**
* Returns the current set of active source IDs.
*/
open fun activeSources(): Set<String> =
activeSources.toSet()
fun activeSources(): Set<String> = activeSources.toSet()

/**
* The source IDs whose entries are visible, or `null` if every source is.
*
* Override this to change what the filter admits; `null` admits every source.
*/
protected open fun visibleSourceIds(): Collection<String>? = activeSources

/**
* Returns true if the source is currently active (visible).
*/
open fun isActive(sourceId: String): Boolean =
sourceId in activeSources
fun isActive(sourceId: String): Boolean = visibleSourceIds()?.contains(sourceId) ?: true

/**
* Returns true if the source exists in the backing index,
* regardless of whether it's active.
*
* Use this to check if a JAR needs indexing at all.
*/
open suspend fun isCached(sourceId: String): Boolean =
backing.containsSource(sourceId)
open suspend fun isCached(sourceId: String): Boolean = backing.containsSource(sourceId)

override fun query(query: IndexQuery): Sequence<T> = backing.query(scopedToActive(query))

/**
* Narrows [query] to the active sources by rewriting its scope, rather than by filtering the
* rows it returns.
*
* Filtering afterwards is wrong whenever the query is limited: the backing index applies the
* limit first, so a page full of inactive rows yields nothing even though matches exist. Pushing
* the active set into the query makes the limit count only rows the caller can actually see.
*/
private fun scopedToActive(query: IndexQuery): IndexQuery {
val visible = visibleSourceIds()?.toSet() ?: return query
Comment thread
itsaky-adfa marked this conversation as resolved.

override fun query(query: IndexQuery): Sequence<T> {
if (query.sourceId != null && !isActive(query.sourceId)) {
return emptySequence()
if (query.sourceId != null) {
// Already as narrow as a scope gets: it either survives the active set or matches nothing.
return if (query.sourceId in visible) query else query.copy(sourceIds = emptyList())
}
val original = backing.query(query)
return original.filter { isActive(it.sourceId) }
}

override suspend fun get(key: String): T? {
val entry = backing.get(key) ?: return null
return if (isActive(entry.sourceId)) entry else null
val requested = query.sourceIds
val scoped = requested?.filter { it in visible } ?: visible
return query.copy(sourceIds = scoped)
}

override suspend fun containsSource(sourceId: String): Boolean {
return isActive(sourceId) && backing.containsSource(sourceId)
}
/**
* Returns the visible entry for [key], the one with the smallest source id among the visible
* sources that have it.
*
* This is a scoped key query rather than a check on the backing index's own [get]: that returns
* the smallest source id overall, and when that source is inactive a visible entry for the same
* key would be missed.
*/
override suspend fun get(key: String): T? =
withContext(Dispatchers.IO) {
backing.query(scopedToActive(IndexQuery.byKey(key))).firstOrNull()
}

override fun distinctValues(fieldName: String): Sequence<String> {
// This is imprecise — the backing index may return values
// from inactive sources. For exact results, we'd need to
// query all entries and filter. For package enumeration
// (the main use case), this approximation is acceptable
// since packages from inactive JARs are harmless — they
// just produce empty results when queried further.
return backing.distinctValues(fieldName)
}
override suspend fun containsSource(sourceId: String): Boolean = isActive(sourceId) && backing.containsSource(sourceId)

override fun distinctValues(
fieldName: String,
query: IndexQuery,
): Sequence<String> = backing.distinctValues(fieldName, scopedToActive(query))

override fun close() {
activeSources.clear()
Expand Down
Loading
Loading