11package org.javacs.kt.index
22
3- import org.jetbrains.exposed.sql.and
4- import org.jetbrains.exposed.sql.count
5- import org.jetbrains.exposed.sql.deleteAll
6- import org.jetbrains.exposed.sql.innerJoin
7- import org.jetbrains.exposed.sql.replace
8- import org.jetbrains.exposed.sql.select
9- import org.jetbrains.exposed.sql.selectAll
10- import org.jetbrains.exposed.sql.Table
113import org.jetbrains.exposed.sql.transactions.transaction
12- import org.jetbrains.exposed.sql.SchemaUtils
134import org.jetbrains.kotlin.descriptors.ModuleDescriptor
145import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
156import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
16- import org.jetbrains.kotlin.resolve.scopes.MemberScope
177import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
188import org.jetbrains.kotlin.name.FqName
19- import org.jetbrains.kotlin.psi2ir.intermediate.extensionReceiverType
209import org.javacs.kt.LOG
2110import org.javacs.kt.progress.Progress
22- import org.jetbrains.exposed.sql.Database
23- import org.jetbrains.exposed.sql.SqlExpressionBuilder.like
24- import org.jetbrains.exposed.sql.insert
25-
26- private val MAX_FQNAME_LENGTH = 255
27- private val MAX_SHORT_NAME_LENGTH = 80
28-
29- private object Symbols : Table() {
30- val fqName = varchar(" fqname" , length = MAX_FQNAME_LENGTH ) references FqNames .fqName
11+ import org.jetbrains.exposed.dao.IntEntity
12+ import org.jetbrains.exposed.dao.IntEntityClass
13+ import org.jetbrains.exposed.dao.id.EntityID
14+ import org.jetbrains.exposed.dao.id.IntIdTable
15+ import org.jetbrains.exposed.sql.*
16+ import java.util.concurrent.CompletableFuture
17+ import kotlin.sequences.Sequence
18+
19+ private const val MAX_FQNAME_LENGTH = 255
20+ private const val MAX_SHORT_NAME_LENGTH = 80
21+ private const val MAX_URI_LENGTH = 511
22+
23+ private object Symbols : IntIdTable() {
24+ val fqName = varchar(" fqname" , length = MAX_FQNAME_LENGTH ).index()
25+ val shortName = varchar(" shortname" , length = MAX_SHORT_NAME_LENGTH )
3126 val kind = integer(" kind" )
3227 val visibility = integer(" visibility" )
3328 val extensionReceiverType = varchar(" extensionreceivertype" , length = MAX_FQNAME_LENGTH ).nullable()
29+ val location = optReference(" location" , Locations )
30+ }
3431
35- override val primaryKey = PrimaryKey (fqName)
32+ private object Locations : IntIdTable() {
33+ val uri = varchar(" uri" , length = MAX_URI_LENGTH )
34+ val range = reference(" range" , Ranges )
3635}
3736
38- private object FqNames : Table() {
39- val fqName = varchar(" fqname" , length = MAX_FQNAME_LENGTH )
40- val shortName = varchar(" shortname" , length = MAX_SHORT_NAME_LENGTH )
37+ private object Ranges : IntIdTable() {
38+ val start = reference(" start" , Positions )
39+ val end = reference(" end" , Positions )
40+ }
41+
42+ private object Positions : IntIdTable() {
43+ val line = integer(" line" )
44+ val character = integer(" character" )
45+ }
46+
47+ class SymbolEntity (id : EntityID <Int >) : IntEntity(id) {
48+ companion object : IntEntityClass <SymbolEntity >(Symbols )
49+
50+ var fqName by Symbols .fqName
51+ var shortName by Symbols .shortName
52+ var kind by Symbols .kind
53+ var visibility by Symbols .visibility
54+ var extensionReceiverType by Symbols .extensionReceiverType
55+ var location by LocationEntity optionalReferencedOn Symbols .location
56+ }
57+
58+ class LocationEntity (id : EntityID <Int >) : IntEntity(id) {
59+ companion object : IntEntityClass <LocationEntity >(Locations )
4160
42- override val primaryKey = PrimaryKey (fqName)
61+ var uri by Locations .uri
62+ var range by RangeEntity referencedOn Locations .range
63+ }
64+
65+ class RangeEntity (id : EntityID <Int >) : IntEntity(id) {
66+ companion object : IntEntityClass <RangeEntity >(Ranges )
67+
68+ var start by PositionEntity referencedOn Ranges .start
69+ var end by PositionEntity referencedOn Ranges .end
70+ }
71+
72+ class PositionEntity (id : EntityID <Int >) : IntEntity(id) {
73+ companion object : IntEntityClass <PositionEntity >(Positions )
74+
75+ var line by Positions .line
76+ var character by Positions .character
4377}
4478
4579/* *
@@ -48,47 +82,49 @@ private object FqNames : Table() {
4882class SymbolIndex {
4983 private val db = Database .connect(" jdbc:h2:mem:symbolindex;DB_CLOSE_DELAY=-1" , " org.h2.Driver" )
5084
51- var progressFactory: Progress .Factory = Progress .Factory .None
85+ var progressFactory: Progress .Factory = object : Progress .Factory {
86+ override fun create (label : String ): CompletableFuture <Progress > = CompletableFuture .supplyAsync { Progress .None }
87+ }
5288
5389 init {
5490 transaction(db) {
55- SchemaUtils .create(Symbols , FqNames )
91+ SchemaUtils .create(Symbols , Locations , Ranges , Positions )
5692 }
5793 }
5894
5995 /* * Rebuilds the entire index. May take a while. */
60- fun refresh (module : ModuleDescriptor ) {
96+ fun refresh (module : ModuleDescriptor , exclusions : Sequence <DeclarationDescriptor >) {
97+ val started = System .currentTimeMillis()
98+ LOG .info(" Updating full symbol index..." )
99+
100+ progressFactory.create(" Indexing" ).thenApplyAsync { progress ->
101+ try {
102+ transaction(db) {
103+ addDeclarations(allDescriptors(module, exclusions))
104+
105+ val finished = System .currentTimeMillis()
106+ val count = Symbols .slice(Symbols .fqName.count()).selectAll().first()[Symbols .fqName.count()]
107+ LOG .info(" Updated full symbol index in ${finished - started} ms! (${count} symbol(s))" )
108+ }
109+ } catch (e: Exception ) {
110+ LOG .error(" Error while updating symbol index" )
111+ LOG .printStackTrace(e)
112+ }
113+
114+ progress.close()
115+ }
116+ }
117+
118+ // Removes a list of indexes and adds another list. Everything is done in the same transaction.
119+ fun updateIndexes (remove : Sequence <DeclarationDescriptor >, add : Sequence <DeclarationDescriptor >) {
61120 val started = System .currentTimeMillis()
62121 LOG .info(" Updating symbol index..." )
63122
64- progressFactory.create(" Indexing" ).thenApply { progress ->
123+ progressFactory.create(" Indexing" ).thenApplyAsync { progress ->
65124 try {
66- // TODO: Incremental updates
67125 transaction(db) {
68- Symbols .deleteAll()
69-
70- for (descriptor in allDescriptors(module)) {
71- val descriptorFqn = descriptor.fqNameSafe
72- val extensionReceiverFqn = descriptor.accept(ExtractSymbolExtensionReceiverType , Unit )?.takeIf { ! it.isRoot }
73-
74- if (canStoreFqName(descriptorFqn) && (extensionReceiverFqn?.let { canStoreFqName(it) } ? : true )) {
75- for (fqn in listOf (descriptorFqn, extensionReceiverFqn).filterNotNull()) {
76- FqNames .replace {
77- it[fqName] = fqn.toString()
78- it[shortName] = fqn.shortName().toString()
79- }
80- }
81-
82- Symbols .replace {
83- it[fqName] = descriptorFqn.toString()
84- it[kind] = descriptor.accept(ExtractSymbolKind , Unit ).rawValue
85- it[visibility] = descriptor.accept(ExtractSymbolVisibility , Unit ).rawValue
86- it[extensionReceiverType] = extensionReceiverFqn?.toString()
87- }
88- } else {
89- LOG .warn(" Excluding symbol {} from index since its name is too long" , descriptorFqn.toString())
90- }
91- }
126+ removeDeclarations(remove)
127+ addDeclarations(add)
92128
93129 val finished = System .currentTimeMillis()
94130 val count = Symbols .slice(Symbols .fqName.count()).selectAll().first()[Symbols .fqName.count()]
@@ -103,29 +139,68 @@ class SymbolIndex {
103139 }
104140 }
105141
106- private fun canStoreFqName (fqName : FqName ) =
107- fqName.toString().length <= MAX_FQNAME_LENGTH
108- && fqName.shortName().toString().length <= MAX_SHORT_NAME_LENGTH
142+ private fun Transaction.removeDeclarations (declarations : Sequence <DeclarationDescriptor >) =
143+ declarations.forEach { declaration ->
144+ val (descriptorFqn, extensionReceiverFqn) = getFqNames(declaration)
145+
146+ if (validFqName(descriptorFqn) && (extensionReceiverFqn?.let { validFqName(it) } != false )) {
147+ Symbols .deleteWhere {
148+ (Symbols .fqName eq descriptorFqn.toString()) and (Symbols .extensionReceiverType eq extensionReceiverFqn?.toString())
149+ }
150+ } else {
151+ LOG .warn(" Excluding symbol {} from index since its name is too long" , descriptorFqn.toString())
152+ }
153+ }
154+
155+ private fun Transaction.addDeclarations (declarations : Sequence <DeclarationDescriptor >) =
156+ declarations.forEach { declaration ->
157+ val (descriptorFqn, extensionReceiverFqn) = getFqNames(declaration)
158+
159+ if (validFqName(descriptorFqn) && (extensionReceiverFqn?.let { validFqName(it) } != false )) {
160+ SymbolEntity .new {
161+ fqName = descriptorFqn.toString()
162+ shortName = descriptorFqn.shortName().toString()
163+ kind = declaration.accept(ExtractSymbolKind , Unit ).rawValue
164+ visibility = declaration.accept(ExtractSymbolVisibility , Unit ).rawValue
165+ extensionReceiverType = extensionReceiverFqn?.toString()
166+ }
167+ } else {
168+ LOG .warn(" Excluding symbol {} from index since its name is too long" , descriptorFqn.toString())
169+ }
170+ }
171+
172+ private fun getFqNames (declaration : DeclarationDescriptor ): Pair <FqName , FqName ?> {
173+ val descriptorFqn = declaration.fqNameSafe
174+ val extensionReceiverFqn = declaration.accept(ExtractSymbolExtensionReceiverType , Unit )?.takeIf { ! it.isRoot }
175+
176+ return Pair (descriptorFqn, extensionReceiverFqn)
177+ }
178+
179+ private fun validFqName (fqName : FqName ) =
180+ fqName.toString().length <= MAX_FQNAME_LENGTH
181+ && fqName.shortName().toString().length <= MAX_SHORT_NAME_LENGTH
109182
110183 fun query (prefix : String , receiverType : FqName ? = null, limit : Int = 20): List <Symbol > = transaction(db) {
111184 // TODO: Extension completion currently only works if the receiver matches exactly,
112185 // ideally this should work with subtypes as well
113- ( Symbols innerJoin FqNames )
114- .select { FqNames . shortName. like( " $prefix %" ) and (Symbols .extensionReceiverType eq receiverType?.toString()) }
115- .limit(limit)
186+ SymbolEntity .find {
187+ ( Symbols . shortName like " $prefix %" ) and (Symbols .extensionReceiverType eq receiverType?.toString())
188+ } .limit(limit)
116189 .map { Symbol (
117- fqName = FqName (it[ Symbols .fqName] ),
118- kind = Symbol .Kind .fromRaw(it[ Symbols .kind] ),
119- visibility = Symbol .Visibility .fromRaw(it[ Symbols .visibility] ),
120- extensionReceiverType = it[ Symbols .extensionReceiverType] ?.let (::FqName )
190+ fqName = FqName (it.fqName),
191+ kind = Symbol .Kind .fromRaw(it.kind),
192+ visibility = Symbol .Visibility .fromRaw(it.visibility),
193+ extensionReceiverType = it.extensionReceiverType?.let (::FqName )
121194 ) }
122195 }
123196
124- private fun allDescriptors (module : ModuleDescriptor ): Sequence <DeclarationDescriptor > = allPackages(module)
197+ private fun allDescriptors (module : ModuleDescriptor , exclusions : Sequence < DeclarationDescriptor > ): Sequence <DeclarationDescriptor > = allPackages(module)
125198 .map(module::getPackage)
126199 .flatMap {
127200 try {
128- it.memberScope.getContributedDescriptors(DescriptorKindFilter .ALL , MemberScope .ALL_NAME_FILTER )
201+ it.memberScope.getContributedDescriptors(
202+ DescriptorKindFilter .ALL
203+ ) { name -> ! exclusions.any { declaration -> declaration.name == name } }
129204 } catch (e: IllegalStateException ) {
130205 LOG .warn(" Could not query descriptors in package $it " )
131206 emptyList()
0 commit comments