1
1
package org.javacs.kt.index
2
2
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
11
3
import org.jetbrains.exposed.sql.transactions.transaction
12
- import org.jetbrains.exposed.sql.SchemaUtils
13
4
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
14
5
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
15
6
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
16
- import org.jetbrains.kotlin.resolve.scopes.MemberScope
17
7
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
18
8
import org.jetbrains.kotlin.name.FqName
19
- import org.jetbrains.kotlin.psi2ir.intermediate.extensionReceiverType
20
9
import org.javacs.kt.LOG
21
10
import 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 )
31
26
val kind = integer(" kind" )
32
27
val visibility = integer(" visibility" )
33
28
val extensionReceiverType = varchar(" extensionreceivertype" , length = MAX_FQNAME_LENGTH ).nullable()
29
+ val location = optReference(" location" , Locations )
30
+ }
34
31
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 )
36
35
}
37
36
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 )
41
60
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
43
77
}
44
78
45
79
/* *
@@ -48,47 +82,49 @@ private object FqNames : Table() {
48
82
class SymbolIndex {
49
83
private val db = Database .connect(" jdbc:h2:mem:symbolindex;DB_CLOSE_DELAY=-1" , " org.h2.Driver" )
50
84
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
+ }
52
88
53
89
init {
54
90
transaction(db) {
55
- SchemaUtils .create(Symbols , FqNames )
91
+ SchemaUtils .create(Symbols , Locations , Ranges , Positions )
56
92
}
57
93
}
58
94
59
95
/* * 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 >) {
61
120
val started = System .currentTimeMillis()
62
121
LOG .info(" Updating symbol index..." )
63
122
64
- progressFactory.create(" Indexing" ).thenApply { progress ->
123
+ progressFactory.create(" Indexing" ).thenApplyAsync { progress ->
65
124
try {
66
- // TODO: Incremental updates
67
125
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)
92
128
93
129
val finished = System .currentTimeMillis()
94
130
val count = Symbols .slice(Symbols .fqName.count()).selectAll().first()[Symbols .fqName.count()]
@@ -103,29 +139,68 @@ class SymbolIndex {
103
139
}
104
140
}
105
141
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
109
182
110
183
fun query (prefix : String , receiverType : FqName ? = null, limit : Int = 20): List <Symbol > = transaction(db) {
111
184
// TODO: Extension completion currently only works if the receiver matches exactly,
112
185
// 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)
116
189
.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 )
121
194
) }
122
195
}
123
196
124
- private fun allDescriptors (module : ModuleDescriptor ): Sequence <DeclarationDescriptor > = allPackages(module)
197
+ private fun allDescriptors (module : ModuleDescriptor , exclusions : Sequence < DeclarationDescriptor > ): Sequence <DeclarationDescriptor > = allPackages(module)
125
198
.map(module::getPackage)
126
199
.flatMap {
127
200
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 } }
129
204
} catch (e: IllegalStateException ) {
130
205
LOG .warn(" Could not query descriptors in package $it " )
131
206
emptyList()
0 commit comments