diff --git a/CHANGELOG.md b/CHANGELOG.md index bab12828f..19ba1d447 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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] diff --git a/src/main/kotlin/io/snyk/plugin/services/SnykTaskQueueService.kt b/src/main/kotlin/io/snyk/plugin/services/SnykTaskQueueService.kt index d26fa6169..6da732987 100644 --- a/src/main/kotlin/io/snyk/plugin/services/SnykTaskQueueService.kt +++ b/src/main/kotlin/io/snyk/plugin/services/SnykTaskQueueService.kt @@ -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()) } diff --git a/src/main/kotlin/io/snyk/plugin/snykcode/SnykCodeBulkFileListener.kt b/src/main/kotlin/io/snyk/plugin/snykcode/SnykCodeBulkFileListener.kt index e0d52cdce..aeb428439 100644 --- a/src/main/kotlin/io/snyk/plugin/snykcode/SnykCodeBulkFileListener.kt +++ b/src/main/kotlin/io/snyk/plugin/snykcode/SnykCodeBulkFileListener.kt @@ -79,9 +79,12 @@ class SnykCodeBulkFileListener : SnykBulkFileListener() { } override fun forwardEvents(events: MutableList) { + 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!! diff --git a/src/main/kotlin/snyk/code/annotator/SnykCodeAnnotatorLS.kt b/src/main/kotlin/snyk/code/annotator/SnykCodeAnnotatorLS.kt index 4d09b7dfc..f579be2f5 100644 --- a/src/main/kotlin/snyk/code/annotator/SnykCodeAnnotatorLS.kt +++ b/src/main/kotlin/snyk/code/annotator/SnykCodeAnnotatorLS.kt @@ -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 @@ -43,7 +44,6 @@ class SnykCodeAnnotatorLS : ExternalAnnotator() { // 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 } @@ -53,8 +53,12 @@ class SnykCodeAnnotatorLS : ExternalAnnotator() { 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() diff --git a/src/main/kotlin/snyk/common/AnnotatorCommon.kt b/src/main/kotlin/snyk/common/AnnotatorCommon.kt index e9de74873..531f7b30e 100644 --- a/src/main/kotlin/snyk/common/AnnotatorCommon.kt +++ b/src/main/kotlin/snyk/common/AnnotatorCommon.kt @@ -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() 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 } @@ -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) + } } - }) + ) } } diff --git a/src/main/kotlin/snyk/common/lsp/LSCodeVisionProvider.kt b/src/main/kotlin/snyk/common/lsp/LSCodeVisionProvider.kt index 11458acd6..f2ab2f91f 100644 --- a/src/main/kotlin/snyk/common/lsp/LSCodeVisionProvider.kt +++ b/src/main/kotlin/snyk/common/lsp/LSCodeVisionProvider.kt @@ -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 @@ -43,8 +44,11 @@ class LSCodeVisionProvider : CodeVisionProvider { } 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 { val project = editor.project ?: return@compute CodeVisionState.READY_EMPTY val document = editor.document diff --git a/src/main/kotlin/snyk/common/lsp/LanguageServerWrapper.kt b/src/main/kotlin/snyk/common/lsp/LanguageServerWrapper.kt index 30b08b69e..ef0fa8e61 100644 --- a/src/main/kotlin/snyk/common/lsp/LanguageServerWrapper.kt +++ b/src/main/kotlin/snyk/common/lsp/LanguageServerWrapper.kt @@ -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 @@ -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 { @@ -179,7 +178,7 @@ class LanguageServerWrapper( fun updateWorkspaceFolders(added: Set, removed: Set) { try { - ensureLanguageServerInitialized() + if (!ensureLanguageServerInitialized()) return val params = DidChangeWorkspaceFoldersParams() params.event = WorkspaceFoldersChangeEvent(added.toList(), removed.toList()) languageServer.workspaceService.didChangeWorkspaceFolders(params) @@ -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() @@ -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.")