Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
55c0ab3
Add SymbolIndex
fwcd Feb 26, 2021
2b88be4
Query descriptors in module in SymbolIndex
fwcd Feb 26, 2021
668eb85
Index available symbols asynchronously
fwcd Feb 26, 2021
bfaa9ce
Lock symbol index before updates
fwcd Feb 26, 2021
17e2317
Include global completions (WIP)
fwcd Feb 26, 2021
3967a8f
Update the index whenever the container changes
fwcd Feb 26, 2021
e86784f
Filter META-INF
fwcd Feb 26, 2021
8442efe
Catch all exceptions in SymbolIndex (for now)
fwcd Feb 26, 2021
2039254
Update Kotlin compiler to 1.4.30-RC-232 (WIP)
fwcd Feb 26, 2021
34fa4ff
Fix language extension issue (for now)
fwcd Feb 26, 2021
2aedd27
Catch errors with indexed symbols on the package level
fwcd Feb 26, 2021
f6efbe0
Move symbol index to subpackage
fwcd Feb 26, 2021
885ec1b
Use ConcurrentHashMap in SymbolIndex
fwcd Feb 26, 2021
ed3905f
Add H2
fwcd Feb 26, 2021
3682e30
Catch errors during indexing again
fwcd Feb 26, 2021
a3baf09
Add exposed
fwcd Feb 26, 2021
87d7fa2
Add custom symbol model for index
fwcd Feb 26, 2021
42829cb
Store symbol index in in-memory H2 database (WIP)
fwcd Feb 26, 2021
799690a
Add SymbolIndex.query
fwcd Feb 26, 2021
60da1cf
Update completions to query symbol index db
fwcd Feb 26, 2021
8b766df
Fix typo
fwcd Feb 26, 2021
d8cb533
Remove autoincrement from Symbols model
fwcd Feb 26, 2021
3e1a5b0
Keep DB table alive
fwcd Feb 26, 2021
01ab459
Clear symbols table before readding entries
fwcd Feb 26, 2021
20674d3
Add workaround for missing insertIgnore
fwcd Feb 26, 2021
228071d
Update completion list logic
fwcd Feb 26, 2021
4380c93
Check whether element completions are 'exhaustive'
fwcd Feb 26, 2021
be23840
Fix project/plugin accessor classpath query for Kotlin DSL
fwcd Feb 27, 2021
a6fb6b7
WIP: Auto-import symbol from index
fwcd Feb 26, 2021
52f9b5d
Find an import insertion position automatically using the AST
fwcd Feb 26, 2021
8b6631d
Ignore global module completions for now
fwcd Feb 26, 2021
691bf81
Only index normal Kotlin files
fwcd Feb 27, 2021
342efbc
Add config option for enabling/disabling indexing
fwcd Feb 27, 2021
1355290
Disable indexer in unit tests (for now)
fwcd Feb 27, 2021
2164f14
Find import position based on longest matching package
fwcd Feb 27, 2021
8fb9a9c
Only suggest unimported index completions
fwcd Feb 27, 2021
bcd620a
Use short names when checking index completions
fwcd Feb 27, 2021
3be9702
Add keyword completions whenever element completions are unavailable
fwcd Feb 27, 2021
cf0e2c5
Generate extra newline for import if none are present
fwcd Feb 27, 2021
d89e079
Filter out index completion labels that are already in scope
fwcd Feb 27, 2021
03a1a93
Add Progress interface
fwcd Feb 27, 2021
275115f
Implement LanguageClientProgress
fwcd Feb 27, 2021
b32f536
Add LanguageClientProgress.Factory
fwcd Feb 27, 2021
2198743
Use new Progress abstraction in initialization
fwcd Feb 27, 2021
29042c4
Create empty implementations of Progress(.Factory)
fwcd Feb 27, 2021
b5f5d33
Create progress for indexing
fwcd Feb 27, 2021
38157fc
Only assign progress factory if client supports work done progress
fwcd Feb 27, 2021
8cf9e0e
Update license report
fwcd Feb 27, 2021
9f6f260
Use safe calls when processing client capabilities
fwcd Feb 27, 2021
c5fcacb
Pass IndexingConfiguration to SourcePath
fwcd Feb 27, 2021
92b791e
Store symbol visibilities in index
fwcd Feb 27, 2021
109f91e
Check visibility in index completions
fwcd Feb 27, 2021
8306dd7
Handle wildcard imports in index completions
fwcd Feb 27, 2021
a5abcf3
Only provide index completions on name references and type elements
fwcd Feb 27, 2021
f0b497c
Move index initialization logic into SourcePath
fwcd Feb 27, 2021
3eb183e
Pass receiver to index completions (WIP)
fwcd Feb 27, 2021
f8a8500
Store extension receiver types in symbol index
fwcd Feb 27, 2021
cbb7bdf
Update Exposed
fwcd Feb 27, 2021
ba65300
Add experimental extension completion using symbol index
fwcd Feb 27, 2021
99776c0
Simplify symbol index query
fwcd Feb 27, 2021
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
3 changes: 2 additions & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
projectVersion=0.9.1
kotlinVersion=1.4.20-release-327
kotlinVersion=1.4.30-RC-232
exposedVersion=0.29.1
javaVersion=11
9 changes: 8 additions & 1 deletion server/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ startScripts {
repositories {
maven { url uri("$projectDir/lib") }
maven { url 'https://jitpack.io' }
// TODO: Update once https://github.com/JetBrains/Exposed/issues/1160 is resolved
// since Bintray will be shutting down soon
maven { url 'https://cache-redirector.jetbrains.com/dl.bintray.com/kotlin/exposed' }
}

dependencies {
Expand All @@ -38,9 +41,13 @@ dependencies {
implementation "org.jetbrains.kotlin:kotlin-scripting-compiler-impl:$kotlinVersion"
implementation "org.jetbrains.kotlin:kotlin-scripting-jvm-host-unshaded:$kotlinVersion"
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion"
implementation "org.jetbrains.kotlin:ide-common-ij201:$kotlinVersion"
implementation "org.jetbrains.kotlin:ide-common-ij202:$kotlinVersion"
// implementation("org.jetbrains.kotlin:kotlin-plugin-ij201:$kotlinVersion") { transitive = false }
implementation 'org.jetbrains:fernflower:1.0'
implementation "org.jetbrains.exposed:exposed-core:$exposedVersion"
implementation "org.jetbrains.exposed:exposed-dao:$exposedVersion"
implementation "org.jetbrains.exposed:exposed-jdbc:$exposedVersion"
implementation 'com.h2database:h2:1.4.200'
implementation 'com.github.fwcd:ktfmt:22bd538a1c'
implementation 'com.beust:jcommander:1.78'

Expand Down
18 changes: 16 additions & 2 deletions server/src/main/dist/licenseReport.html
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ <h3>Notice for packages:</h3>
</li>
<pre>GNU LESSER GENERAL PUBLIC LICENSE 2.1
<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>
<li>
<a href='#1544977522'>Joda-Time</a>
</li>
<li>
<a href='#1544977522'>Kotlinx-coroutines-core</a>
</li>
Expand Down Expand Up @@ -428,10 +431,18 @@ <h3>Notice for packages:</h3>
See the License for the specific language governing permissions and
limitations under the License.
</pre>
<li>
<a href='#76480'>Exposed</a>
</li>
<li>
<a href='#76480'>Ktfmt</a>
</li>
<pre>No license found</pre>
<li>
<a href='#1168029172'>H2 Database Engine</a>
</li>
<pre>MPL 2.0 or EPL 1.0
<a href='https://h2database.com/html/license.html'>https://h2database.com/html/license.html</a></pre>
<li>
<a href='#-989315363'>Checker Qual</a>
</li>
Expand Down Expand Up @@ -545,7 +556,7 @@ <h3>Notice for packages:</h3>
<a href='#1288284111'>Kotlin Util Klib</a>
</li>
<li>
<a href='#1288284111'>Org.jetbrains.kotlin:ide-common-ij201</a>
<a href='#1288284111'>Org.jetbrains.kotlin:ide-common-ij202</a>
</li>
<a name='1288284111' />
<pre> Apache License
Expand Down Expand Up @@ -758,7 +769,10 @@ <h3>Notice for packages:</h3>
<li>
<a href='#-687391964'>Animal Sniffer Annotations</a>
</li>
<pre>MIT license
<li>
<a href='#-687391964'>SLF4J API Module</a>
</li>
<pre>MIT License
<a href='http://www.opensource.org/licenses/mit-license.php'>http://www.opensource.org/licenses/mit-license.php</a></pre>
<li>
<a href='#79718298'>LSP4J</a>
Expand Down
6 changes: 6 additions & 0 deletions server/src/main/kotlin/org/javacs/kt/Configuration.kt
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ public data class CompilerConfiguration(
val jvm: JVMConfiguration = JVMConfiguration()
)

public data class IndexingConfiguration(
/** Whether an index of global symbols should be built in the background. */
var enabled: Boolean = true
)

public data class ExternalSourcesConfiguration(
/** Whether kls-URIs should be sent to the client to describe classes in JARs. */
var useKlsScheme: Boolean = false,
Expand All @@ -34,5 +39,6 @@ public data class Configuration(
val compiler: CompilerConfiguration = CompilerConfiguration(),
val completion: CompletionConfiguration = CompletionConfiguration(),
val linting: LintingConfiguration = LintingConfiguration(),
var indexing: IndexingConfiguration = IndexingConfiguration(),
val externalSources: ExternalSourcesConfiguration = ExternalSourcesConfiguration()
)
44 changes: 17 additions & 27 deletions server/src/main/kotlin/org/javacs/kt/KotlinLanguageServer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,27 +11,35 @@ import org.javacs.kt.externalsources.JarClassContentProvider
import org.javacs.kt.util.AsyncExecutor
import org.javacs.kt.util.TemporaryDirectory
import org.javacs.kt.util.parseURI
import org.javacs.kt.progress.Progress
import org.javacs.kt.progress.LanguageClientProgress
import java.net.URI
import java.io.Closeable
import java.nio.file.Paths
import java.util.concurrent.CompletableFuture
import java.util.concurrent.CompletableFuture.completedFuture

class KotlinLanguageServer : LanguageServer, LanguageClientAware, Closeable {
private val config = Configuration()
val config = Configuration()
val classPath = CompilerClassPath(config.compiler)

private val tempDirectory = TemporaryDirectory()
private val uriContentProvider = URIContentProvider(JarClassContentProvider(config.externalSources, classPath, tempDirectory))
val sourcePath = SourcePath(classPath, uriContentProvider)
val sourcePath = SourcePath(classPath, uriContentProvider, config.indexing)
val sourceFiles = SourceFiles(sourcePath, uriContentProvider)

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

private lateinit var client: LanguageClient

private val async = AsyncExecutor()
private var progressFactory: Progress.Factory = Progress.Factory.None
set(factory: Progress.Factory) {
field = factory
sourcePath.progressFactory = factory
}

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

val folders = params.workspaceFolders

fun reportProgress(notification: WorkDoneProgressNotification) {
params.workDoneToken?.let {
client.notifyProgress(ProgressParams(it, notification))
}
if (clientCapabilities?.window?.workDoneProgress ?: false) {
progressFactory = LanguageClientProgress.Factory(client)
}

reportProgress(WorkDoneProgressBegin().apply {
title = "Adding Kotlin workspace folders"
percentage = 0
})
val folders = params.workspaceFolders
val progress = params.workDoneToken?.let { LanguageClientProgress("Workspace folders", it, client) }

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

reportProgress(WorkDoneProgressReport().apply {
message = "$progressPrefix: Updating source path"
percentage = progressPercent
})

progress?.update("$progressPrefix: Updating source path", progressPercent)
val root = Paths.get(parseURI(folder.uri))
sourceFiles.addWorkspaceRoot(root)

reportProgress(WorkDoneProgressReport().apply {
message = "$progressPrefix: Updating class path"
percentage = progressPercent
})

progress?.update("$progressPrefix: Updating class path", progressPercent)
val refreshed = classPath.addWorkspaceRoot(root)
if (refreshed) {
reportProgress(WorkDoneProgressReport().apply {
message = "$progressPrefix: Refreshing source path"
percentage = progressPercent
})

progress?.update("$progressPrefix: Refreshing source path", progressPercent)
sourcePath.refresh()
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ class KotlinTextDocumentService(
LOG.info("Completing at {}", describePosition(position))

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

Either.forRight<List<CompletionItem>, CompletionList>(completions)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,14 @@ class KotlinWorkspaceService(
}
}

// Update indexing options
get("indexing")?.asJsonObject?.apply {
val indexing = config.indexing
get("enabled")?.asBoolean?.let {
indexing.enabled = it
}
}

// Update options about external sources e.g. JAR files, decompilers, etc
get("externalSources")?.asJsonObject?.apply {
val externalSources = config.externalSources
Expand Down
43 changes: 41 additions & 2 deletions server/src/main/kotlin/org/javacs/kt/SourcePath.kt
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
package org.javacs.kt

import org.javacs.kt.compiler.CompilationKind
import org.javacs.kt.util.AsyncExecutor
import org.javacs.kt.util.fileExtension
import org.javacs.kt.util.filePath
import org.javacs.kt.util.describeURI
import org.javacs.kt.index.SymbolIndex
import org.javacs.kt.progress.Progress
import org.javacs.kt.IndexingConfiguration
import com.intellij.lang.Language
import com.intellij.psi.PsiFile
import com.intellij.openapi.fileTypes.FileType
import com.intellij.openapi.fileTypes.LanguageFileType
import org.jetbrains.kotlin.container.ComponentProvider
import org.jetbrains.kotlin.container.getService
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.CompositeBindingContext
Expand All @@ -18,13 +26,25 @@ import java.util.concurrent.locks.ReentrantLock

class SourcePath(
private val cp: CompilerClassPath,
private val contentProvider: URIContentProvider
private val contentProvider: URIContentProvider,
private val indexingConfig: IndexingConfiguration
) {
private val files = mutableMapOf<URI, SourceFile>()
private val parseDataWriteLock = ReentrantLock()

private val indexAsync = AsyncExecutor()
private var indexInitialized: Boolean = false
var indexEnabled: Boolean by indexingConfig::enabled
val index = SymbolIndex()

var beforeCompileCallback: () -> Unit = {}

var progressFactory: Progress.Factory = Progress.Factory.None
set(factory: Progress.Factory) {
field = factory
index.progressFactory = factory
}

private inner class SourceFile(
val uri: URI,
var content: String,
Expand All @@ -36,7 +56,7 @@ class SourcePath(
val language: Language? = null,
val isTemporary: Boolean = false // A temporary source file will not be returned by .all()
) {
val extension: String? = uri.fileExtension ?: language?.associatedFileType?.defaultExtension
val extension: String? = uri.fileExtension ?: "kt" // TODO: Use language?.associatedFileType?.defaultExtension again
val isScript: Boolean = extension == "kts"
val kind: CompilationKind =
if (path?.fileName?.toString()?.endsWith(".gradle.kts") ?: false) CompilationKind.BUILD_SCRIPT
Expand Down Expand Up @@ -85,6 +105,8 @@ class SourcePath(
compiledContainer = container
compiledFile = parsed
}

initializeIndexAsyncIfNeeded(container)
}

private fun doCompileIfChanged() {
Expand Down Expand Up @@ -190,6 +212,11 @@ class SourcePath(
}
}

// Only index normal files, not build files
if (kind == CompilationKind.DEFAULT) {
initializeIndexAsyncIfNeeded(container)
}

return context
}

Expand All @@ -203,6 +230,18 @@ class SourcePath(
return CompositeBindingContext.create(combined)
}

/**
* Initialized the symbol index asynchronously, if not
* already done.
*/
private fun initializeIndexAsyncIfNeeded(container: ComponentProvider) = indexAsync.execute {
if (indexEnabled && !indexInitialized) {
indexInitialized = true
val module = container.getService(ModuleDescriptor::class.java)
index.refresh(module)
}
}

/**
* Recompiles all source files that are initialized.
*/
Expand Down
Loading