Skip to content

Commit

Permalink
fix: UI freezes & initialization errors (#486)
Browse files Browse the repository at this point in the history
* fix: UI freeze & initialization checks [IDE-184]

* docs: update CHANGELOG.md

* chore: remove superfluous call

* chore: add LS initialization to AnnotatorCommon.prepareAnnotate

* chore: linting fun

* fix: annotator should filter by issue id
  • Loading branch information
bastiandoetsch authored Mar 11, 2024
1 parent 8ff9452 commit 7706e10
Show file tree
Hide file tree
Showing 7 changed files with 40 additions and 19 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## [2.7.8]
### Fixed
- (LS Preview) UI freezes and initialization errors caused by CodeVision and Code annotations
- (LS Preview) check trust for content root before triggering Snyk Code scans

## [2.7.7]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,6 @@ class SnykTaskQueueService(val project: Project) {
fun connectProjectToLanguageServer(project: Project) {
synchronized(LanguageServerWrapper) {
val wrapper = LanguageServerWrapper.getInstance()
wrapper.ensureLanguageServerInitialized()
val added = wrapper.getWorkspaceFolders(project)
wrapper.updateWorkspaceFolders(added, emptySet())
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,12 @@ class SnykCodeBulkFileListener : SnykBulkFileListener() {
}

override fun forwardEvents(events: MutableList<out VFileEvent>) {
val languageServerWrapper = LanguageServerWrapper.getInstance()

if (!isSnykCodeLSEnabled()) return
LanguageServerWrapper.getInstance().ensureLanguageServerInitialized()
val languageServer = LanguageServerWrapper.getInstance().languageServer
if (!languageServerWrapper.ensureLanguageServerInitialized()) return

val languageServer = languageServerWrapper.languageServer
for (event in events) {
if (event.file == null || !event.isFromSave) continue
val file = event.file!!
Expand Down
6 changes: 5 additions & 1 deletion src/main/kotlin/snyk/code/annotator/SnykCodeAnnotatorLS.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import icons.SnykIcons
import io.snyk.plugin.Severity
import io.snyk.plugin.getSnykCachedResults
import io.snyk.plugin.isSnykCodeLSEnabled
import io.snyk.plugin.isSnykCodeRunning
import io.snyk.plugin.ui.toolwindow.SnykToolWindowPanel
import org.eclipse.lsp4j.CodeAction
import org.eclipse.lsp4j.CodeActionContext
Expand Down Expand Up @@ -43,7 +44,6 @@ class SnykCodeAnnotatorLS : ExternalAnnotator<PsiFile, Unit>() {

// overrides needed for the Annotator to invoke apply(). We don't do anything here
override fun collectInformation(file: PsiFile): PsiFile {
LanguageServerWrapper.getInstance().ensureLanguageServerInitialized()
return file
}

Expand All @@ -53,8 +53,12 @@ class SnykCodeAnnotatorLS : ExternalAnnotator<PsiFile, Unit>() {

override fun apply(psiFile: PsiFile, annotationResult: Unit, holder: AnnotationHolder) {
if (!isSnykCodeLSEnabled()) return
if (!LanguageServerWrapper.getInstance().ensureLanguageServerInitialized()) return
if (isSnykCodeRunning(psiFile.project)) return

getIssuesForFile(psiFile)
.filter { AnnotatorCommon.isSeverityToShow(it.getSeverityAsEnum()) }
.distinctBy { it.id }
.sortedBy { it.title }
.forEach { issue ->
val highlightSeverity = issue.getSeverityAsEnum().getHighlightSeverity()
Expand Down
26 changes: 18 additions & 8 deletions src/main/kotlin/snyk/common/AnnotatorCommon.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,17 @@ import io.snyk.plugin.events.SnykProductsOrSeverityListener
import io.snyk.plugin.events.SnykSettingsListener
import io.snyk.plugin.pluginSettings
import io.snyk.plugin.refreshAnnotationsForOpenFiles
import snyk.common.lsp.LanguageServerWrapper

object AnnotatorCommon {
val logger = logger<AnnotatorCommon>()

fun prepareAnnotate(psiFile: PsiFile?) {
logger.debug("Preparing annotation for $psiFile")

// trigger LS initialization if not already done, we consciously don't check the result here
LanguageServerWrapper.getInstance().ensureLanguageServerInitialized()

// todo: review later if any way to provide up-to-date context for CLI scans is available
// force saving here will break some user's workflow: https://github.com/snyk/snyk-intellij-plugin/issues/324
}
Expand All @@ -25,16 +30,21 @@ object AnnotatorCommon {
logger.debug("Initializing annotations refresh listener")
project.messageBus.connect()
.subscribe(
SnykProductsOrSeverityListener.SNYK_ENABLEMENT_TOPIC, object : SnykProductsOrSeverityListener {
override fun enablementChanged() {
refreshAnnotationsForOpenFiles(project)
SnykProductsOrSeverityListener.SNYK_ENABLEMENT_TOPIC,
object : SnykProductsOrSeverityListener {
override fun enablementChanged() {
refreshAnnotationsForOpenFiles(project)
}
}
})
)
project.messageBus.connect()
.subscribe(SnykSettingsListener.SNYK_SETTINGS_TOPIC, object : SnykSettingsListener {
override fun settingsChanged() {
refreshAnnotationsForOpenFiles(project)
.subscribe(
SnykSettingsListener.SNYK_SETTINGS_TOPIC,
object : SnykSettingsListener {
override fun settingsChanged() {
refreshAnnotationsForOpenFiles(project)
}
}
})
)
}
}
6 changes: 5 additions & 1 deletion src/main/kotlin/snyk/common/lsp/LSCodeVisionProvider.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import com.intellij.openapi.util.TextRange
import com.intellij.psi.PsiDocumentManager
import icons.SnykIcons
import io.snyk.plugin.isSnykCodeLSEnabled
import io.snyk.plugin.isSnykCodeRunning
import org.eclipse.lsp4j.CodeLens
import org.eclipse.lsp4j.CodeLensParams
import org.eclipse.lsp4j.ExecuteCommandParams
Expand All @@ -43,8 +44,11 @@ class LSCodeVisionProvider : CodeVisionProvider<Unit> {
}

override fun computeCodeVision(editor: Editor, uiData: Unit): CodeVisionState {
if (editor.project == null) return CodeVisionState.READY_EMPTY
if (!isSnykCodeLSEnabled()) return CodeVisionState.READY_EMPTY
LanguageServerWrapper.getInstance().ensureLanguageServerInitialized()
if (!LanguageServerWrapper.getInstance().ensureLanguageServerInitialized()) return CodeVisionState.READY_EMPTY
if (isSnykCodeRunning(editor.project!!)) return CodeVisionState.READY_EMPTY

return ReadAction.compute<CodeVisionState, RuntimeException> {
val project = editor.project ?: return@compute CodeVisionState.READY_EMPTY
val document = editor.document
Expand Down
12 changes: 6 additions & 6 deletions src/main/kotlin/snyk/common/lsp/LanguageServerWrapper.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import io.snyk.plugin.getContentRootVirtualFiles
import io.snyk.plugin.getUserAgentString
import io.snyk.plugin.isSnykCodeLSEnabled
import io.snyk.plugin.pluginSettings
import io.snyk.plugin.ui.SnykBalloonNotificationHelper
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
Expand Down Expand Up @@ -90,7 +89,7 @@ class LanguageServerWrapper(
internal fun initialize() {
if (lsPath.toNioPathOrNull()?.exists() == false) {
val message = "Snyk Language Server not found. Please make sure the Snyk CLI is installed at $lsPath."
SnykBalloonNotificationHelper.showError(message, null)
logger.warn(message)
return
}
try {
Expand Down Expand Up @@ -179,7 +178,7 @@ class LanguageServerWrapper(

fun updateWorkspaceFolders(added: Set<WorkspaceFolder>, removed: Set<WorkspaceFolder>) {
try {
ensureLanguageServerInitialized()
if (!ensureLanguageServerInitialized()) return
val params = DidChangeWorkspaceFoldersParams()
params.event = WorkspaceFoldersChangeEvent(added.toList(), removed.toList())
languageServer.workspaceService.didChangeWorkspaceFolders(params)
Expand All @@ -188,17 +187,18 @@ class LanguageServerWrapper(
}
}

fun ensureLanguageServerInitialized() {
fun ensureLanguageServerInitialized(): Boolean {
while (isInitializing) {
Thread.sleep(DEFAULT_SLEEP_TIME)
}
if (!isInitialized) {
initialize()
}
return isInitialized
}

fun sendReportAnalyticsCommand(scanDoneEvent: ScanDoneEvent) {
ensureLanguageServerInitialized()
if (!ensureLanguageServerInitialized()) return
try {
val eventString = gson.toJson(scanDoneEvent)
val param = ExecuteCommandParams()
Expand All @@ -211,7 +211,7 @@ class LanguageServerWrapper(
}

fun sendScanCommand() {
ensureLanguageServerInitialized()
if (!ensureLanguageServerInitialized()) return
val project = ProjectUtil.getActiveProject()
if (project == null) {
logger.warn("No active project found, not sending scan command.")
Expand Down

0 comments on commit 7706e10

Please sign in to comment.