fix: anchor LSP diagnostics to server readiness
This commit is contained in:
+36
-8
@@ -146,23 +146,52 @@ class Lsp4jDiagnosticsRunner(
|
||||
|
||||
/**
|
||||
* Block until every opened URI has received at least one push (or the timeout elapses), then
|
||||
* a short settle window so servers that push an empty report first, real diagnostics second
|
||||
* (tsserver does this after project load) land their final result before we read it.
|
||||
* wait for a quiet period anchored to the last server publication. Servers such as tsserver
|
||||
* can publish an empty report while loading the project and real diagnostics afterward; a
|
||||
* timer started immediately after didOpen reads the workspace before the server is ready.
|
||||
*/
|
||||
private fun awaitDiagnostics(client: CollectingLanguageClient, uris: Set<String>) {
|
||||
val deadline = System.nanoTime() + TimeUnit.SECONDS.toNanos(timeoutSeconds)
|
||||
while (System.nanoTime() < deadline && !client.hasAll(uris)) {
|
||||
Thread.sleep(POLL_MS)
|
||||
}
|
||||
Thread.sleep(SETTLE_MS)
|
||||
client.awaitAll(uris, deadline)
|
||||
client.awaitQuiet(deadline, TimeUnit.MILLISECONDS.toNanos(SETTLE_MILLIS))
|
||||
}
|
||||
|
||||
private class CollectingLanguageClient : LanguageClient {
|
||||
private val byUri = ConcurrentHashMap<String, List<Diagnostic>>()
|
||||
private val updates = Object()
|
||||
@Volatile private var lastUpdateNanos = 0L
|
||||
fun latestFor(uri: String): List<Diagnostic> = byUri[uri].orEmpty()
|
||||
fun hasAll(uris: Set<String>): Boolean = byUri.keys.containsAll(uris)
|
||||
|
||||
fun awaitAll(uris: Set<String>, deadline: Long) {
|
||||
synchronized(updates) {
|
||||
while (!hasAll(uris)) {
|
||||
val remaining = deadline - System.nanoTime()
|
||||
if (remaining <= 0) return
|
||||
updates.wait(TimeUnit.NANOSECONDS.toMillis(remaining).coerceAtLeast(1))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun awaitQuiet(deadline: Long, quietNanos: Long) {
|
||||
synchronized(updates) {
|
||||
while (true) {
|
||||
val remaining = deadline - System.nanoTime()
|
||||
if (remaining <= 0) return
|
||||
val sinceUpdate = System.nanoTime() - lastUpdateNanos
|
||||
if (lastUpdateNanos != 0L && sinceUpdate >= quietNanos) return
|
||||
val waitNanos = minOf(remaining, quietNanos - sinceUpdate)
|
||||
updates.wait(TimeUnit.NANOSECONDS.toMillis(waitNanos).coerceAtLeast(1))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun publishDiagnostics(diagnostics: PublishDiagnosticsParams) {
|
||||
byUri[diagnostics.uri] = diagnostics.diagnostics.orEmpty()
|
||||
synchronized(updates) {
|
||||
lastUpdateNanos = System.nanoTime()
|
||||
updates.notifyAll()
|
||||
}
|
||||
}
|
||||
override fun telemetryEvent(`object`: Any?) = Unit
|
||||
override fun showMessage(messageParams: MessageParams) = Unit
|
||||
@@ -172,7 +201,6 @@ class Lsp4jDiagnosticsRunner(
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val POLL_MS = 100L
|
||||
const val SETTLE_MS = 750L
|
||||
const val SETTLE_MILLIS = 750L
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user