-
Notifications
You must be signed in to change notification settings - Fork 34
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor: move files around, add progress impl from LSP4IJ
- Loading branch information
1 parent
5e12077
commit f7865da
Showing
16 changed files
with
298 additions
and
22 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
/******************************************************************************* | ||
* Copyright (c) 2024 Red Hat, Inc. | ||
* Copyright (c) 2024 Snyk Ltd | ||
* | ||
* Distributed under license by Red Hat, Inc. All rights reserved. | ||
* This program is made available under the terms of the | ||
* Eclipse Public License v2.0 which accompanies this distribution, | ||
* and is available at http://www.eclipse.org/legal/epl-v20.html | ||
* | ||
* SPDX-License-Identifier: EPL-2.0 | ||
* | ||
* Contributors: | ||
* Red Hat, Inc. - initial API and implementation | ||
* Snyk Ltd - adjustments for use in Snyk IntelliJ Plugin | ||
*******************************************************************************/ | ||
package snyk.common.lsp.progress | ||
|
||
import org.eclipse.lsp4j.WorkDoneProgressNotification | ||
import java.util.concurrent.LinkedBlockingDeque | ||
import java.util.concurrent.TimeUnit | ||
|
||
internal class Progress(val token: String) { | ||
var cancellable: Boolean = false | ||
var done: Boolean = false | ||
|
||
private val progressNotifications = LinkedBlockingDeque<WorkDoneProgressNotification>() | ||
|
||
var cancelled: Boolean = false | ||
private set | ||
|
||
var title: String? = null | ||
get() = if (field != null) field else token | ||
|
||
fun add(progressNotification: WorkDoneProgressNotification) { | ||
progressNotifications.add(progressNotification) | ||
} | ||
|
||
@get:Throws(InterruptedException::class) | ||
val nextProgressNotification: WorkDoneProgressNotification? | ||
get() = progressNotifications.pollFirst(200, TimeUnit.MILLISECONDS) | ||
|
||
fun cancel() { | ||
this.cancelled = true | ||
} | ||
} |
220 changes: 220 additions & 0 deletions
220
src/main/kotlin/snyk/common/lsp/progress/ProgressManager.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,220 @@ | ||
/******************************************************************************* | ||
* Copyright (c) 2024 Red Hat, Inc. | ||
* Copyright (c) 2024 Snyk Ltd | ||
* | ||
* Distributed under license by Red Hat, Inc. All rights reserved. | ||
* This program is made available under the terms of the | ||
* Eclipse Public License v2.0 which accompanies this distribution, | ||
* and is available at http://www.eclipse.org/legal/epl-v20.html | ||
* | ||
* SPDX-License-Identifier: EPL-2.0 | ||
* | ||
* Contributors: | ||
* Red Hat, Inc. - initial API and implementation | ||
* Snyk Ltd - adjustments for use in Snyk IntelliJ Plugin | ||
*******************************************************************************/ | ||
package snyk.common.lsp.progress | ||
|
||
|
||
import com.intellij.ide.impl.ProjectUtil | ||
import com.intellij.openapi.Disposable | ||
import com.intellij.openapi.progress.ProcessCanceledException | ||
import com.intellij.openapi.progress.ProgressIndicator | ||
import com.intellij.openapi.progress.ProgressManager | ||
import com.intellij.openapi.progress.Task | ||
import io.snyk.plugin.ui.toolwindow.SnykPluginDisposable | ||
import org.eclipse.lsp4j.ProgressParams | ||
import org.eclipse.lsp4j.WorkDoneProgressBegin | ||
import org.eclipse.lsp4j.WorkDoneProgressCancelParams | ||
import org.eclipse.lsp4j.WorkDoneProgressCreateParams | ||
import org.eclipse.lsp4j.WorkDoneProgressKind | ||
import org.eclipse.lsp4j.WorkDoneProgressNotification | ||
import org.eclipse.lsp4j.WorkDoneProgressReport | ||
import org.eclipse.lsp4j.jsonrpc.messages.Either | ||
import snyk.common.lsp.LanguageServerWrapper | ||
import java.util.concurrent.CompletableFuture | ||
import java.util.concurrent.ConcurrentHashMap | ||
import java.util.function.Function | ||
|
||
|
||
class ProgressManager() : Disposable { | ||
private val progresses: MutableMap<String, Progress> = ConcurrentHashMap<String, Progress>() | ||
private var disposed = false | ||
get() { | ||
return SnykPluginDisposable.getInstance().isDisposed() || field | ||
} | ||
|
||
fun isDisposed() = disposed | ||
|
||
fun createProgress(params: WorkDoneProgressCreateParams): CompletableFuture<Void> { | ||
if (!disposed) { | ||
val token = getToken(params.token) | ||
getProgress(token) | ||
} | ||
return CompletableFuture.completedFuture(null) | ||
} | ||
|
||
private fun createProgressIndicator(progress: Progress) { | ||
val token: String = progress.token | ||
if (isDone(progress)) { | ||
progresses.remove(token) | ||
return | ||
} | ||
val title = "Snyk: " + progress.title | ||
val cancellable: Boolean = progress.cancellable | ||
ProgressManager.getInstance() | ||
.run(newProgressBackgroundTask(title, cancellable, progress, token)) | ||
} | ||
|
||
private fun newProgressBackgroundTask( | ||
title: String, | ||
cancellable: Boolean, | ||
progress: Progress, | ||
token: String | ||
) = object : Task.Backgroundable(ProjectUtil.getActiveProject(), title, cancellable) { | ||
override fun run(indicator: ProgressIndicator) { | ||
try { | ||
while (!isDone(progress)) { | ||
if (indicator.isCanceled) { | ||
progresses.remove(token) | ||
val workDoneProgressCancelParams = WorkDoneProgressCancelParams() | ||
workDoneProgressCancelParams.setToken(token) | ||
val languageServerWrapper = LanguageServerWrapper.getInstance() | ||
if (languageServerWrapper.isInitialized) { | ||
val languageServer = languageServerWrapper.languageServer | ||
languageServer.cancelProgress(workDoneProgressCancelParams) | ||
} | ||
throw ProcessCanceledException() | ||
} | ||
|
||
var progressNotification: WorkDoneProgressNotification? | ||
try { | ||
progressNotification = progress.nextProgressNotification | ||
} catch (e: InterruptedException) { | ||
progresses.remove(token) | ||
Thread.currentThread().interrupt() | ||
throw ProcessCanceledException(e) | ||
} | ||
if (progressNotification != null) { | ||
val kind = progressNotification.kind ?: return | ||
when (kind) { | ||
WorkDoneProgressKind.begin -> // 'begin' has been notified | ||
begin(progressNotification as WorkDoneProgressBegin, indicator) | ||
|
||
WorkDoneProgressKind.report -> // 'report' has been notified | ||
report(progressNotification as WorkDoneProgressReport, indicator) | ||
|
||
WorkDoneProgressKind.end -> Unit | ||
} | ||
} | ||
} | ||
} finally { | ||
progresses.remove(token) | ||
} | ||
} | ||
} | ||
|
||
private fun isDone(progress: Progress): Boolean { | ||
return progress.done || progress.cancelled || disposed | ||
} | ||
|
||
private fun begin( | ||
begin: WorkDoneProgressBegin, | ||
progressIndicator: ProgressIndicator | ||
) { | ||
val percentage = begin.percentage | ||
progressIndicator.isIndeterminate = percentage == null | ||
updateProgressIndicator(begin.message, percentage, progressIndicator) | ||
} | ||
|
||
private fun report( | ||
report: WorkDoneProgressReport, | ||
progressIndicator: ProgressIndicator | ||
) { | ||
updateProgressIndicator(report.message, report.percentage, progressIndicator) | ||
} | ||
|
||
@Synchronized | ||
private fun getProgress(token: String): Progress { | ||
var progress: Progress? = progresses[token] | ||
if (progress != null) { | ||
return progress | ||
} | ||
progress = Progress(token) | ||
progresses[token] = progress | ||
return progress | ||
} | ||
|
||
private fun updateProgressIndicator( | ||
message: String?, | ||
percentage: Int?, | ||
progressIndicator: ProgressIndicator | ||
) { | ||
if (!message.isNullOrBlank()) { | ||
progressIndicator.text = message | ||
} | ||
if (percentage != null) { | ||
progressIndicator.fraction = percentage.toDouble() / 100 | ||
} | ||
} | ||
|
||
fun notifyProgress(params: ProgressParams) { | ||
if (params.value == null || params.token == null || disposed) { | ||
return | ||
} | ||
val value = params.value | ||
if (value.isRight) { | ||
// we don't need partial results progress support | ||
return | ||
} | ||
|
||
if (!value.isLeft) { | ||
return | ||
} | ||
|
||
val progressNotification = value.left | ||
val kind = progressNotification.kind ?: return | ||
val token = getToken(params.token) | ||
var progress: Progress? = progresses[token] | ||
if (progress == null) { | ||
// The server is not spec-compliant and reports progress using server-initiated progress but didn't | ||
// call window/workDoneProgress/create beforehand. In that case, we check the 'kind' field of the | ||
// progress data. If the 'kind' field is 'begin', we set up a progress reporter anyway. | ||
if (kind != WorkDoneProgressKind.begin) { | ||
return | ||
} | ||
progress = getProgress(token) | ||
} | ||
|
||
// Add the progress notification | ||
progress.add(progressNotification) | ||
when (progressNotification.kind!!) { | ||
WorkDoneProgressKind.begin -> { | ||
// 'begin' progress | ||
val begin = progressNotification as WorkDoneProgressBegin | ||
progress.title = begin.title | ||
progress.cancellable = begin.cancellable != null && begin.cancellable | ||
// The IJ task is created on 'begin' and not on 'create' to initialize | ||
// the Task with the 'begin' title. | ||
createProgressIndicator(progress) | ||
} | ||
|
||
WorkDoneProgressKind.end -> progress.done = true | ||
WorkDoneProgressKind.report -> Unit | ||
} | ||
} | ||
|
||
override fun dispose() { | ||
this.disposed = true | ||
progresses.values.forEach(Progress::cancel) | ||
progresses.clear() | ||
} | ||
|
||
companion object { | ||
private fun getToken(token: Either<String, Int>): String { | ||
return token.map( | ||
Function.identity() | ||
) { obj: Int -> obj.toString() } | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
3 changes: 2 additions & 1 deletion
3
...snyk/common/lsp/LanguageServerSettings.kt → ...on/lsp/settings/LanguageServerSettings.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.