Skip to content

Commit 6506474

Browse files
authored
Merge pull request #268 from fwcd/symbol-index
Index global descriptors and provide completions for unimported symbols
2 parents 571303b + 99776c0 commit 6506474

18 files changed

+546
-46
lines changed

gradle.properties

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
projectVersion=0.9.1
2-
kotlinVersion=1.4.20-release-327
2+
kotlinVersion=1.4.30-RC-232
3+
exposedVersion=0.29.1
34
javaVersion=11

server/build.gradle

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ startScripts {
2727
repositories {
2828
maven { url uri("$projectDir/lib") }
2929
maven { url 'https://jitpack.io' }
30+
// TODO: Update once https://github.com/JetBrains/Exposed/issues/1160 is resolved
31+
// since Bintray will be shutting down soon
32+
maven { url 'https://cache-redirector.jetbrains.com/dl.bintray.com/kotlin/exposed' }
3033
}
3134

3235
dependencies {
@@ -38,9 +41,13 @@ dependencies {
3841
implementation "org.jetbrains.kotlin:kotlin-scripting-compiler-impl:$kotlinVersion"
3942
implementation "org.jetbrains.kotlin:kotlin-scripting-jvm-host-unshaded:$kotlinVersion"
4043
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion"
41-
implementation "org.jetbrains.kotlin:ide-common-ij201:$kotlinVersion"
44+
implementation "org.jetbrains.kotlin:ide-common-ij202:$kotlinVersion"
4245
// implementation("org.jetbrains.kotlin:kotlin-plugin-ij201:$kotlinVersion") { transitive = false }
4346
implementation 'org.jetbrains:fernflower:1.0'
47+
implementation "org.jetbrains.exposed:exposed-core:$exposedVersion"
48+
implementation "org.jetbrains.exposed:exposed-dao:$exposedVersion"
49+
implementation "org.jetbrains.exposed:exposed-jdbc:$exposedVersion"
50+
implementation 'com.h2database:h2:1.4.200'
4451
implementation 'com.github.fwcd:ktfmt:22bd538a1c'
4552
implementation 'com.beust:jcommander:1.78'
4653

server/src/main/dist/licenseReport.html

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ <h3>Notice for packages:</h3>
1616
</li>
1717
<pre>GNU LESSER GENERAL PUBLIC LICENSE 2.1
1818
<a href='https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html'>https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html</a></pre>
19+
<li>
20+
<a href='#1544977522'>Joda-Time</a>
21+
</li>
1922
<li>
2023
<a href='#1544977522'>Kotlinx-coroutines-core</a>
2124
</li>
@@ -428,10 +431,18 @@ <h3>Notice for packages:</h3>
428431
See the License for the specific language governing permissions and
429432
limitations under the License.
430433
</pre>
434+
<li>
435+
<a href='#76480'>Exposed</a>
436+
</li>
431437
<li>
432438
<a href='#76480'>Ktfmt</a>
433439
</li>
434440
<pre>No license found</pre>
441+
<li>
442+
<a href='#1168029172'>H2 Database Engine</a>
443+
</li>
444+
<pre>MPL 2.0 or EPL 1.0
445+
<a href='https://h2database.com/html/license.html'>https://h2database.com/html/license.html</a></pre>
435446
<li>
436447
<a href='#-989315363'>Checker Qual</a>
437448
</li>
@@ -545,7 +556,7 @@ <h3>Notice for packages:</h3>
545556
<a href='#1288284111'>Kotlin Util Klib</a>
546557
</li>
547558
<li>
548-
<a href='#1288284111'>Org.jetbrains.kotlin:ide-common-ij201</a>
559+
<a href='#1288284111'>Org.jetbrains.kotlin:ide-common-ij202</a>
549560
</li>
550561
<a name='1288284111' />
551562
<pre> Apache License
@@ -758,7 +769,10 @@ <h3>Notice for packages:</h3>
758769
<li>
759770
<a href='#-687391964'>Animal Sniffer Annotations</a>
760771
</li>
761-
<pre>MIT license
772+
<li>
773+
<a href='#-687391964'>SLF4J API Module</a>
774+
</li>
775+
<pre>MIT License
762776
<a href='http://www.opensource.org/licenses/mit-license.php'>http://www.opensource.org/licenses/mit-license.php</a></pre>
763777
<li>
764778
<a href='#79718298'>LSP4J</a>

server/src/main/kotlin/org/javacs/kt/Configuration.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,11 @@ public data class CompilerConfiguration(
2323
val jvm: JVMConfiguration = JVMConfiguration()
2424
)
2525

26+
public data class IndexingConfiguration(
27+
/** Whether an index of global symbols should be built in the background. */
28+
var enabled: Boolean = true
29+
)
30+
2631
public data class ExternalSourcesConfiguration(
2732
/** Whether kls-URIs should be sent to the client to describe classes in JARs. */
2833
var useKlsScheme: Boolean = false,
@@ -34,5 +39,6 @@ public data class Configuration(
3439
val compiler: CompilerConfiguration = CompilerConfiguration(),
3540
val completion: CompletionConfiguration = CompletionConfiguration(),
3641
val linting: LintingConfiguration = LintingConfiguration(),
42+
var indexing: IndexingConfiguration = IndexingConfiguration(),
3743
val externalSources: ExternalSourcesConfiguration = ExternalSourcesConfiguration()
3844
)

server/src/main/kotlin/org/javacs/kt/KotlinLanguageServer.kt

Lines changed: 17 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -11,27 +11,35 @@ import org.javacs.kt.externalsources.JarClassContentProvider
1111
import org.javacs.kt.util.AsyncExecutor
1212
import org.javacs.kt.util.TemporaryDirectory
1313
import org.javacs.kt.util.parseURI
14+
import org.javacs.kt.progress.Progress
15+
import org.javacs.kt.progress.LanguageClientProgress
1416
import java.net.URI
1517
import java.io.Closeable
1618
import java.nio.file.Paths
1719
import java.util.concurrent.CompletableFuture
1820
import java.util.concurrent.CompletableFuture.completedFuture
1921

2022
class KotlinLanguageServer : LanguageServer, LanguageClientAware, Closeable {
21-
private val config = Configuration()
23+
val config = Configuration()
2224
val classPath = CompilerClassPath(config.compiler)
2325

2426
private val tempDirectory = TemporaryDirectory()
2527
private val uriContentProvider = URIContentProvider(JarClassContentProvider(config.externalSources, classPath, tempDirectory))
26-
val sourcePath = SourcePath(classPath, uriContentProvider)
28+
val sourcePath = SourcePath(classPath, uriContentProvider, config.indexing)
2729
val sourceFiles = SourceFiles(sourcePath, uriContentProvider)
2830

2931
private val textDocuments = KotlinTextDocumentService(sourceFiles, sourcePath, config, tempDirectory, uriContentProvider)
3032
private val workspaces = KotlinWorkspaceService(sourceFiles, sourcePath, classPath, textDocuments, config)
3133
private val protocolExtensions = KotlinProtocolExtensionService(uriContentProvider)
3234

3335
private lateinit var client: LanguageClient
36+
3437
private val async = AsyncExecutor()
38+
private var progressFactory: Progress.Factory = Progress.Factory.None
39+
set(factory: Progress.Factory) {
40+
field = factory
41+
sourcePath.progressFactory = factory
42+
}
3543

3644
override fun connect(client: LanguageClient) {
3745
this.client = client
@@ -72,44 +80,26 @@ class KotlinLanguageServer : LanguageServer, LanguageClientAware, Closeable {
7280
val clientCapabilities = params.capabilities
7381
config.completion.snippets.enabled = clientCapabilities?.textDocument?.completion?.completionItem?.snippetSupport ?: false
7482

75-
val folders = params.workspaceFolders
76-
77-
fun reportProgress(notification: WorkDoneProgressNotification) {
78-
params.workDoneToken?.let {
79-
client.notifyProgress(ProgressParams(it, notification))
80-
}
83+
if (clientCapabilities?.window?.workDoneProgress ?: false) {
84+
progressFactory = LanguageClientProgress.Factory(client)
8185
}
8286

83-
reportProgress(WorkDoneProgressBegin().apply {
84-
title = "Adding Kotlin workspace folders"
85-
percentage = 0
86-
})
87+
val folders = params.workspaceFolders
88+
val progress = params.workDoneToken?.let { LanguageClientProgress("Workspace folders", it, client) }
8789

8890
folders.forEachIndexed { i, folder ->
8991
LOG.info("Adding workspace folder {}", folder.name)
9092
val progressPrefix = "[${i + 1}/${folders.size}] ${folder.name}"
9193
val progressPercent = (100 * i) / folders.size
9294

93-
reportProgress(WorkDoneProgressReport().apply {
94-
message = "$progressPrefix: Updating source path"
95-
percentage = progressPercent
96-
})
97-
95+
progress?.update("$progressPrefix: Updating source path", progressPercent)
9896
val root = Paths.get(parseURI(folder.uri))
9997
sourceFiles.addWorkspaceRoot(root)
10098

101-
reportProgress(WorkDoneProgressReport().apply {
102-
message = "$progressPrefix: Updating class path"
103-
percentage = progressPercent
104-
})
105-
99+
progress?.update("$progressPrefix: Updating class path", progressPercent)
106100
val refreshed = classPath.addWorkspaceRoot(root)
107101
if (refreshed) {
108-
reportProgress(WorkDoneProgressReport().apply {
109-
message = "$progressPrefix: Refreshing source path"
110-
percentage = progressPercent
111-
})
112-
102+
progress?.update("$progressPrefix: Refreshing source path", progressPercent)
113103
sourcePath.refresh()
114104
}
115105
}

server/src/main/kotlin/org/javacs/kt/KotlinTextDocumentService.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ class KotlinTextDocumentService(
156156
LOG.info("Completing at {}", describePosition(position))
157157

158158
val (file, cursor) = recover(position, Recompile.NEVER) // TODO: Investigate when to recompile
159-
val completions = completions(file, cursor, config.completion)
159+
val completions = completions(file, cursor, sp.index, config.completion)
160160
LOG.info("Found {} items", completions.items.size)
161161

162162
Either.forRight<List<CompletionItem>, CompletionList>(completions)

server/src/main/kotlin/org/javacs/kt/KotlinWorkspaceService.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,14 @@ class KotlinWorkspaceService(
123123
}
124124
}
125125

126+
// Update indexing options
127+
get("indexing")?.asJsonObject?.apply {
128+
val indexing = config.indexing
129+
get("enabled")?.asBoolean?.let {
130+
indexing.enabled = it
131+
}
132+
}
133+
126134
// Update options about external sources e.g. JAR files, decompilers, etc
127135
get("externalSources")?.asJsonObject?.apply {
128136
val externalSources = config.externalSources

server/src/main/kotlin/org/javacs/kt/SourcePath.kt

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,20 @@
11
package org.javacs.kt
22

33
import org.javacs.kt.compiler.CompilationKind
4+
import org.javacs.kt.util.AsyncExecutor
45
import org.javacs.kt.util.fileExtension
56
import org.javacs.kt.util.filePath
67
import org.javacs.kt.util.describeURI
8+
import org.javacs.kt.index.SymbolIndex
9+
import org.javacs.kt.progress.Progress
10+
import org.javacs.kt.IndexingConfiguration
711
import com.intellij.lang.Language
812
import com.intellij.psi.PsiFile
13+
import com.intellij.openapi.fileTypes.FileType
14+
import com.intellij.openapi.fileTypes.LanguageFileType
915
import org.jetbrains.kotlin.container.ComponentProvider
16+
import org.jetbrains.kotlin.container.getService
17+
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
1018
import org.jetbrains.kotlin.psi.KtFile
1119
import org.jetbrains.kotlin.resolve.BindingContext
1220
import org.jetbrains.kotlin.resolve.CompositeBindingContext
@@ -18,13 +26,25 @@ import java.util.concurrent.locks.ReentrantLock
1826

1927
class SourcePath(
2028
private val cp: CompilerClassPath,
21-
private val contentProvider: URIContentProvider
29+
private val contentProvider: URIContentProvider,
30+
private val indexingConfig: IndexingConfiguration
2231
) {
2332
private val files = mutableMapOf<URI, SourceFile>()
2433
private val parseDataWriteLock = ReentrantLock()
2534

35+
private val indexAsync = AsyncExecutor()
36+
private var indexInitialized: Boolean = false
37+
var indexEnabled: Boolean by indexingConfig::enabled
38+
val index = SymbolIndex()
39+
2640
var beforeCompileCallback: () -> Unit = {}
2741

42+
var progressFactory: Progress.Factory = Progress.Factory.None
43+
set(factory: Progress.Factory) {
44+
field = factory
45+
index.progressFactory = factory
46+
}
47+
2848
private inner class SourceFile(
2949
val uri: URI,
3050
var content: String,
@@ -36,7 +56,7 @@ class SourcePath(
3656
val language: Language? = null,
3757
val isTemporary: Boolean = false // A temporary source file will not be returned by .all()
3858
) {
39-
val extension: String? = uri.fileExtension ?: language?.associatedFileType?.defaultExtension
59+
val extension: String? = uri.fileExtension ?: "kt" // TODO: Use language?.associatedFileType?.defaultExtension again
4060
val isScript: Boolean = extension == "kts"
4161
val kind: CompilationKind =
4262
if (path?.fileName?.toString()?.endsWith(".gradle.kts") ?: false) CompilationKind.BUILD_SCRIPT
@@ -85,6 +105,8 @@ class SourcePath(
85105
compiledContainer = container
86106
compiledFile = parsed
87107
}
108+
109+
initializeIndexAsyncIfNeeded(container)
88110
}
89111

90112
private fun doCompileIfChanged() {
@@ -190,6 +212,11 @@ class SourcePath(
190212
}
191213
}
192214

215+
// Only index normal files, not build files
216+
if (kind == CompilationKind.DEFAULT) {
217+
initializeIndexAsyncIfNeeded(container)
218+
}
219+
193220
return context
194221
}
195222

@@ -203,6 +230,18 @@ class SourcePath(
203230
return CompositeBindingContext.create(combined)
204231
}
205232

233+
/**
234+
* Initialized the symbol index asynchronously, if not
235+
* already done.
236+
*/
237+
private fun initializeIndexAsyncIfNeeded(container: ComponentProvider) = indexAsync.execute {
238+
if (indexEnabled && !indexInitialized) {
239+
indexInitialized = true
240+
val module = container.getService(ModuleDescriptor::class.java)
241+
index.refresh(module)
242+
}
243+
}
244+
206245
/**
207246
* Recompiles all source files that are initialized.
208247
*/

0 commit comments

Comments
 (0)