diff --git a/src/main/kotlin/com/github/biomejs/intellijbiome/BiomeBundle.kt b/src/main/kotlin/com/github/biomejs/intellijbiome/BiomeBundle.kt index f039b99..6d8a228 100644 --- a/src/main/kotlin/com/github/biomejs/intellijbiome/BiomeBundle.kt +++ b/src/main/kotlin/com/github/biomejs/intellijbiome/BiomeBundle.kt @@ -11,11 +11,13 @@ object BiomeBundle : DynamicBundle(BUNDLE) { @Suppress("SpreadOperator") @JvmStatic - fun message(@PropertyKey(resourceBundle = BUNDLE) key: String, vararg params: Any) = + fun message(@PropertyKey(resourceBundle = BUNDLE) key: String, + vararg params: Any) = getMessage(key, *params) @Suppress("SpreadOperator", "unused") @JvmStatic - fun messagePointer(@PropertyKey(resourceBundle = BUNDLE) key: String, vararg params: Any) = + fun messagePointer(@PropertyKey(resourceBundle = BUNDLE) key: String, + vararg params: Any) = getLazyMessage(key, *params) } diff --git a/src/main/kotlin/com/github/biomejs/intellijbiome/BiomeConfigIconProvider.kt b/src/main/kotlin/com/github/biomejs/intellijbiome/BiomeConfigIconProvider.kt index 8656871..f296bf3 100644 --- a/src/main/kotlin/com/github/biomejs/intellijbiome/BiomeConfigIconProvider.kt +++ b/src/main/kotlin/com/github/biomejs/intellijbiome/BiomeConfigIconProvider.kt @@ -6,7 +6,8 @@ import com.intellij.psi.PsiFile import javax.swing.Icon class BiomeConfigIconProvider : IconProvider() { - override fun getIcon(element: PsiElement, flags: Int): Icon? { + override fun getIcon(element: PsiElement, + flags: Int): Icon? { val file = element as? PsiFile ?: return null if (!file.isValid || file.isDirectory) return null val virtualFile = file.virtualFile ?: return null diff --git a/src/main/kotlin/com/github/biomejs/intellijbiome/BiomeRunner.kt b/src/main/kotlin/com/github/biomejs/intellijbiome/BiomeRunner.kt deleted file mode 100644 index 8070f76..0000000 --- a/src/main/kotlin/com/github/biomejs/intellijbiome/BiomeRunner.kt +++ /dev/null @@ -1,41 +0,0 @@ -package com.github.biomejs.intellijbiome - -import com.intellij.openapi.editor.Document -import com.intellij.openapi.util.NlsContexts -import com.intellij.openapi.util.NlsSafe -import com.intellij.openapi.vfs.VirtualFile -import org.jetbrains.annotations.Nls -import kotlin.time.Duration -import kotlin.time.Duration.Companion.milliseconds - -enum class Feature { - Format, - SafeFixes, - UnsafeFixes -} - -interface BiomeRunner { - companion object { - val DEFAULT_TIMEOUT: Duration = 30000.milliseconds - } - - suspend fun check(request: Request): Response - - data class Request( - val document: Document, - val virtualFile: VirtualFile, - val timeout: Duration, - @NlsContexts.Command val commandDescription: String - ) - - sealed class Response { - class Success(val code: String) : Response() - - class Failure( - @Nls val title: String, - @NlsSafe val description: String, - val exitCode: Int? - ) : Response() - - } -} diff --git a/src/main/kotlin/com/github/biomejs/intellijbiome/BiomeStdinRunner.kt b/src/main/kotlin/com/github/biomejs/intellijbiome/BiomeStdinRunner.kt deleted file mode 100644 index 3950ee5..0000000 --- a/src/main/kotlin/com/github/biomejs/intellijbiome/BiomeStdinRunner.kt +++ /dev/null @@ -1,94 +0,0 @@ -package com.github.biomejs.intellijbiome - -import com.github.biomejs.intellijbiome.extensions.isSuccess -import com.github.biomejs.intellijbiome.extensions.runProcessFuture -import com.github.biomejs.intellijbiome.settings.BiomeSettings -import com.intellij.execution.ExecutionException -import com.intellij.openapi.project.Project -import com.intellij.util.SmartList -import kotlinx.coroutines.future.await -import java.util.EnumSet - -class BiomeStdinRunner(project: Project) : BiomeRunner { - private val biomePackage = BiomePackage(project) - private val settings = BiomeSettings.getInstance(project) - private val features = settings.getEnabledFeatures() - private val targetRunBuilder = BiomeTargetRunBuilder(project) - - override suspend fun check(request: BiomeRunner.Request): BiomeRunner.Response { - val file = request.virtualFile - val configPath = biomePackage.configPath(file) - val exePath = biomePackage.binaryPath(configPath, false) - ?: throw ExecutionException(BiomeBundle.message("biome.language.server.not.found")) - - val params = SmartList("check", "--stdin-file-path", file.path) - if (!configPath.isNullOrEmpty()) { - params.add("--config-path") - params.add(configPath) - } - params.addAll(getCheckFlags(features, biomePackage)) - - val processHandler = targetRunBuilder.getBuilder(exePath).apply { - if (!configPath.isNullOrEmpty()) { - setWorkingDirectory(configPath) - } - addParameters(params) - setInputFile(file) - }.build() - - val result = runProcessFuture(processHandler).await() - - val processOutput = result.processOutput - val stdout = processOutput.stdout - val stderr = processOutput.stderr.trim() - - if (result.processEvent.isSuccess) { - return BiomeRunner.Response.Success(stdout) - } else { - if (processOutput.isTimeout) { - return BiomeRunner.Response.Failure(BiomeBundle.message("biome.failed.to.run.biome.check.with.features", - features.joinToString(prefix = "(", postfix = ")") { it -> it.toString().lowercase() }, - file.name), "Timeout exceeded", null) - } - - return BiomeRunner.Response.Failure(BiomeBundle.message("biome.failed.to.run.biome.check.with.features", - features.joinToString(prefix = "(", postfix = ")") { it -> it.toString().lowercase() }, - file.name), stderr, processOutput.exitCode) - } - } - - suspend fun getCheckFlags(features: EnumSet, - biomePackage: BiomePackage): List { - val args = SmartList() - - if (features.isEmpty()) return args - - if (features.contains(Feature.Format)) { - args.add("--formatter-enabled=true") - } else { - args.add("--formatter-enabled=false") - } - - if (!features.contains(Feature.SafeFixes) && !features.contains(Feature.UnsafeFixes)) { - args.add("--linter-enabled=false") - } - - val version = biomePackage.versionNumber() - if (version === null || version.isEmpty() || biomePackage.compareVersion(version, "1.8.0") >= 0) { - args.add("--write") - if (features.contains(Feature.UnsafeFixes)) { - args.add("--unsafe") - } - } else { - if (features.contains(Feature.UnsafeFixes)) { - args.add("--apply-unsafe") - } else { - args.add("--apply") - } - } - - args.add("--skip-errors") - - return args - } -} diff --git a/src/main/kotlin/com/github/biomejs/intellijbiome/GeneralProcessCommandBuilder.kt b/src/main/kotlin/com/github/biomejs/intellijbiome/GeneralProcessCommandBuilder.kt index 9dfe3be..6dc71cd 100644 --- a/src/main/kotlin/com/github/biomejs/intellijbiome/GeneralProcessCommandBuilder.kt +++ b/src/main/kotlin/com/github/biomejs/intellijbiome/GeneralProcessCommandBuilder.kt @@ -11,7 +11,8 @@ import java.nio.charset.Charset import kotlin.io.path.Path class GeneralProcessCommandBuilder : ProcessCommandBuilder { - private val command = GeneralCommandLine().withParentEnvironmentType(GeneralCommandLine.ParentEnvironmentType.CONSOLE) + private val command = + GeneralCommandLine().withParentEnvironmentType(GeneralCommandLine.ParentEnvironmentType.CONSOLE) private var executable: String? = null private var workingDir: String? = null private var inputFile: VirtualFile? = null diff --git a/src/main/kotlin/com/github/biomejs/intellijbiome/NodeProcessCommandBuilder.kt b/src/main/kotlin/com/github/biomejs/intellijbiome/NodeProcessCommandBuilder.kt index d1a6e00..e1420b6 100644 --- a/src/main/kotlin/com/github/biomejs/intellijbiome/NodeProcessCommandBuilder.kt +++ b/src/main/kotlin/com/github/biomejs/intellijbiome/NodeProcessCommandBuilder.kt @@ -4,9 +4,9 @@ import com.intellij.execution.ExecutionException import com.intellij.execution.process.OSProcessHandler import com.intellij.execution.target.value.TargetValue import com.intellij.javascript.nodejs.execution.NodeTargetRun -import com.intellij.openapi.project.Project import com.intellij.javascript.nodejs.execution.NodeTargetRunOptions.Companion.of import com.intellij.javascript.nodejs.interpreter.NodeJsInterpreter +import com.intellij.openapi.project.Project import com.intellij.openapi.vfs.VirtualFile import java.nio.charset.Charset diff --git a/src/main/kotlin/com/github/biomejs/intellijbiome/actions/BiomeApplySafeFixesAction.kt b/src/main/kotlin/com/github/biomejs/intellijbiome/actions/BiomeApplySafeFixesAction.kt new file mode 100644 index 0000000..4f71bda --- /dev/null +++ b/src/main/kotlin/com/github/biomejs/intellijbiome/actions/BiomeApplySafeFixesAction.kt @@ -0,0 +1,62 @@ +package com.github.biomejs.intellijbiome.actions + +import com.github.biomejs.intellijbiome.BiomeBundle +import com.github.biomejs.intellijbiome.BiomeIcons +import com.github.biomejs.intellijbiome.services.BiomeServerService +import com.github.biomejs.intellijbiome.services.BiomeServerService.Feature +import com.github.biomejs.intellijbiome.settings.BiomeConfigurable +import com.github.biomejs.intellijbiome.settings.BiomeSettings +import com.intellij.notification.NotificationAction +import com.intellij.notification.NotificationGroupManager +import com.intellij.notification.NotificationType +import com.intellij.openapi.actionSystem.AnAction +import com.intellij.openapi.actionSystem.AnActionEvent +import com.intellij.openapi.actionSystem.CommonDataKeys +import com.intellij.openapi.fileEditor.FileDocumentManager +import com.intellij.openapi.options.ShowSettingsUtil +import com.intellij.openapi.project.DumbAware +import com.intellij.platform.ide.progress.runWithModalProgressBlocking +import kotlinx.coroutines.withTimeout + +class BiomeApplySafeFixesAction : AnAction(), DumbAware { + init { + templatePresentation.icon = BiomeIcons.BiomeIcon + } + + override fun actionPerformed(event: AnActionEvent) { + val project = event.project ?: return + val editor = event.getData(CommonDataKeys.EDITOR) ?: return + + val notificationGroup = NotificationGroupManager.getInstance().getNotificationGroup("Biome") + + val settings = BiomeSettings.getInstance(project) + val manager = FileDocumentManager.getInstance() + val virtualFile = manager.getFile(editor.document) ?: return + + if (!settings.fileSupported(virtualFile)) { + notificationGroup.createNotification(title = BiomeBundle.message("biome.file.not.supported.title"), + content = BiomeBundle.message("biome.file.not.supported.description", virtualFile.name), + type = NotificationType.WARNING) + .addAction(NotificationAction.createSimple(BiomeBundle.message("biome.configure.extensions.link")) { + ShowSettingsUtil.getInstance().showSettingsDialog(project, BiomeConfigurable::class.java) + }).notify(project) + return + } + + runWithModalProgressBlocking(project, + BiomeBundle.message("biome.run.biome.check.with.features", Feature.ApplySafeFixes.toString())) { + try { + withTimeout(5_000) { + BiomeServerService.getInstance(project).applySafeFixes(editor.document) + } + notificationGroup.createNotification(title = BiomeBundle.message("biome.apply.safe.fixes.success.label"), + content = BiomeBundle.message("biome.apply.safe.fixes.success.description"), + type = NotificationType.INFORMATION).notify(project) + } catch (e: Exception) { + notificationGroup.createNotification(title = BiomeBundle.message("biome.apply.safe.fixes.failure.label"), + content = BiomeBundle.message("biome.apply.safe.fixes.failure.description", e.message.toString()), + type = NotificationType.ERROR).notify(project) + } + } + } +} diff --git a/src/main/kotlin/com/github/biomejs/intellijbiome/actions/BiomeCheckOnSaveAction.kt b/src/main/kotlin/com/github/biomejs/intellijbiome/actions/BiomeCheckOnSaveAction.kt index d91816b..77daf7a 100644 --- a/src/main/kotlin/com/github/biomejs/intellijbiome/actions/BiomeCheckOnSaveAction.kt +++ b/src/main/kotlin/com/github/biomejs/intellijbiome/actions/BiomeCheckOnSaveAction.kt @@ -1,18 +1,51 @@ package com.github.biomejs.intellijbiome.actions +import com.github.biomejs.intellijbiome.BiomeBundle +import com.github.biomejs.intellijbiome.services.BiomeServerService import com.github.biomejs.intellijbiome.settings.BiomeSettings import com.intellij.ide.actionsOnSave.impl.ActionsOnSaveFileDocumentManagerListener +import com.intellij.notification.NotificationGroupManager +import com.intellij.notification.NotificationType import com.intellij.openapi.editor.Document +import com.intellij.openapi.fileEditor.FileDocumentManager import com.intellij.openapi.project.Project +import com.intellij.platform.ide.progress.runWithModalProgressBlocking +import kotlinx.coroutines.withTimeout class BiomeCheckOnSaveAction : ActionsOnSaveFileDocumentManagerListener.ActionOnSave() { override fun isEnabledForProject(project: Project): Boolean { - val settings = BiomeSettings.getInstance(project) - - return settings.applySafeFixesOnSave || settings.applyUnsafeFixesOnSave || settings.formatOnSave + return !BiomeSettings.getInstance(project).getEnabledFeatures().isEmpty() } - override fun processDocuments(project: Project, documents: Array) { - BiomeCheckRunner(project).run(documents) + override fun processDocuments(project: Project, + documents: Array) { + val features = BiomeSettings.getInstance(project).getEnabledFeatures() + val featuresInfo = features.joinToString(prefix = "(", postfix = ")") { it.toString().lowercase() } + val notificationGroup = NotificationGroupManager.getInstance().getNotificationGroup("Biome") + + runWithModalProgressBlocking(project, + BiomeBundle.message("biome.run.biome.check.with.features", featuresInfo)) { + try { + withTimeout(5_000) { + documents.filter { + val settings = BiomeSettings.getInstance(project) + val manager = FileDocumentManager.getInstance() + val virtualFile = manager.getFile(it) ?: return@filter false + return@filter settings.fileSupported(virtualFile) + }.forEach { + BiomeServerService.getInstance(project).executeFeatures(it, features) + } + } + } catch (e: Exception) { + notificationGroup.createNotification( + title = BiomeBundle.message("biome.apply.feature.on.save.failure.label", featuresInfo), + content = BiomeBundle.message( + "biome.apply.feature.on.save.failure.description", + featuresInfo, + e.message.toString() + ), + type = NotificationType.ERROR).notify(project) + } + } } } diff --git a/src/main/kotlin/com/github/biomejs/intellijbiome/actions/BiomeCheckRunner.kt b/src/main/kotlin/com/github/biomejs/intellijbiome/actions/BiomeCheckRunner.kt deleted file mode 100644 index 025d77a..0000000 --- a/src/main/kotlin/com/github/biomejs/intellijbiome/actions/BiomeCheckRunner.kt +++ /dev/null @@ -1,120 +0,0 @@ -package com.github.biomejs.intellijbiome.actions - -import com.github.biomejs.intellijbiome.BiomeBundle -import com.github.biomejs.intellijbiome.BiomeRunner -import com.github.biomejs.intellijbiome.BiomeStdinRunner -import com.github.biomejs.intellijbiome.settings.BiomeSettings -import com.intellij.codeStyle.AbstractConvertLineSeparatorsAction -import com.intellij.lang.javascript.linter.GlobPatternUtil -import com.intellij.openapi.application.readAction -import com.intellij.openapi.command.writeCommandAction -import com.intellij.openapi.diagnostic.thisLogger -import com.intellij.openapi.editor.Document -import com.intellij.openapi.fileEditor.FileDocumentManager -import com.intellij.openapi.project.Project -import com.intellij.openapi.util.text.StringUtil -import com.intellij.openapi.vfs.VirtualFile -import com.intellij.platform.ide.progress.runWithModalProgressBlocking -import com.intellij.util.LineSeparator -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.async -import kotlinx.coroutines.awaitAll -import kotlinx.coroutines.withTimeout - -class BiomeCheckRunner( - val project: Project, -) { - private val LOG = thisLogger() - private val settings = BiomeSettings.getInstance(project) - private val description: String by lazy { - BiomeBundle.message("biome.run.biome.check.with.features", - settings.getEnabledFeatures().joinToString(prefix = "(", postfix = ")") { it -> it.toString().lowercase() }) - } - - fun run(documents: Array) { - runWithModalProgressBlocking( - project, - BiomeBundle.message( - "biome.run.biome.check.with.features", - settings.getEnabledFeatures().joinToString(prefix = "(", postfix = ")") { it -> it.toString().lowercase() } - ) - ) { - withTimeout(5_000) { - val jobs = documents.map { - async(Dispatchers.IO) { - processDocument(it) - } - } - jobs.awaitAll() - } - } - } - - suspend fun processDocument(document: Document) { - val request = getRequest(document) ?: return - val runner = BiomeStdinRunner(project) - val response = runner.check(request) - applyChanges(request, response) - } - - private suspend fun getRequest(document: Document): BiomeRunner.Request? { - return readAction { - val manager = FileDocumentManager.getInstance() - val file = manager.getFile(document) ?: return@readAction null - - if (!GlobPatternUtil.isFileMatchingGlobPattern(project, settings.filePattern, file)) { - return@readAction null - } - - val request = BiomeRunner.Request(document, file, BiomeRunner.DEFAULT_TIMEOUT, description) - return@readAction request - } - } - - private suspend fun applyChanges( - request: BiomeRunner.Request, - response: BiomeRunner.Response, - ) { - when (response) { - is BiomeRunner.Response.Success -> { - val text = response.code - val lineSeparator = StringUtil.detectSeparators(text) - // internally we keep newlines as \n - val normalizedText = StringUtil.convertLineSeparators(text) - writeCommandAction(project, request.commandDescription) { - if (!StringUtil.equals(request.document.charsSequence, normalizedText)) { - request.document.setText(normalizedText) - } - - setDetectedLineSeparator(project, request.virtualFile, lineSeparator) - } - } - - is BiomeRunner.Response.Failure -> { - LOG.error("${response.title} - ${response.description}") - } - } - } - - /** - * [Taken from the JetBrains Prettier Plugin](https://github.com/JetBrains/intellij-plugins/blob/5673be79dd9e0fff7ed98e58a7d071a5a5f96d87/prettierJS/src/com/intellij/prettierjs/ReformatWithPrettierAction.java#L486) - * [Apache License 2.0](https://github.com/JetBrains/intellij-plugins/blob/5673be79dd9e0fff7ed98e58a7d071a5a5f96d87/prettierJS/LICENSE.TXT) - * - * @return true if the line separators were updated - */ - private fun setDetectedLineSeparator( - project: Project, - vFile: VirtualFile, - newSeparator: LineSeparator?, - ): Boolean { - if (newSeparator != null) { - val newSeparatorString: String = newSeparator.separatorString - - if (!StringUtil.equals(vFile.detectedLineSeparator, newSeparatorString)) { - AbstractConvertLineSeparatorsAction.changeLineSeparators(project, vFile, newSeparatorString) - return true - } - } - return false - } -} diff --git a/src/main/kotlin/com/github/biomejs/intellijbiome/actions/BiomeSortImportAction.kt b/src/main/kotlin/com/github/biomejs/intellijbiome/actions/BiomeSortImportAction.kt new file mode 100644 index 0000000..fd6c721 --- /dev/null +++ b/src/main/kotlin/com/github/biomejs/intellijbiome/actions/BiomeSortImportAction.kt @@ -0,0 +1,63 @@ +package com.github.biomejs.intellijbiome.actions + +import com.github.biomejs.intellijbiome.BiomeBundle +import com.github.biomejs.intellijbiome.BiomeIcons +import com.github.biomejs.intellijbiome.services.BiomeServerService +import com.github.biomejs.intellijbiome.services.BiomeServerService.Feature +import com.github.biomejs.intellijbiome.settings.BiomeConfigurable +import com.github.biomejs.intellijbiome.settings.BiomeSettings +import com.intellij.notification.NotificationAction +import com.intellij.notification.NotificationGroupManager +import com.intellij.notification.NotificationType +import com.intellij.openapi.actionSystem.AnAction +import com.intellij.openapi.actionSystem.AnActionEvent +import com.intellij.openapi.actionSystem.CommonDataKeys +import com.intellij.openapi.fileEditor.FileDocumentManager +import com.intellij.openapi.options.ShowSettingsUtil +import com.intellij.openapi.project.DumbAware +import com.intellij.platform.ide.progress.runWithModalProgressBlocking +import kotlinx.coroutines.withTimeout + +class BiomeSortImportAction : AnAction(), DumbAware { + init { + templatePresentation.icon = BiomeIcons.BiomeIcon + } + + override fun actionPerformed(event: AnActionEvent) { + val project = event.project ?: return + val editor = event.getData(CommonDataKeys.EDITOR) ?: return + + val notificationGroup = NotificationGroupManager.getInstance().getNotificationGroup("Biome") + + val settings = BiomeSettings.getInstance(project) + val manager = FileDocumentManager.getInstance() + val virtualFile = manager.getFile(editor.document) ?: return + + if (!settings.fileSupported(virtualFile)) { + notificationGroup.createNotification(title = BiomeBundle.message("biome.file.not.supported.title"), + content = BiomeBundle.message("biome.file.not.supported.description", virtualFile.name), + type = NotificationType.WARNING) + .addAction(NotificationAction.createSimple(BiomeBundle.message("biome.configure.extensions.link")) { + ShowSettingsUtil.getInstance().showSettingsDialog(project, BiomeConfigurable::class.java) + }).notify(project) + return + } + + runWithModalProgressBlocking(project, + BiomeBundle.message("biome.run.biome.check.with.features", Feature.SortImports.toString())) { + try { + withTimeout(5_000) { + BiomeServerService.getInstance(project).sortImports(editor.document) + } + notificationGroup.createNotification(title = BiomeBundle.message("biome.apply.sort.import.success.label"), + content = BiomeBundle.message("biome.apply.sort.import.success.description"), + type = NotificationType.INFORMATION).notify(project) + } catch (e: Exception) { + notificationGroup.createNotification(title = BiomeBundle.message("biome.apply.sort.import.failure.label"), + content = BiomeBundle.message("biome.apply.sort.import.failure.description", e.message.toString()), + type = NotificationType.ERROR).notify(project) + } + } + } +} + diff --git a/src/main/kotlin/com/github/biomejs/intellijbiome/extensions/CapturingProcessAdapterExt.kt b/src/main/kotlin/com/github/biomejs/intellijbiome/extensions/CapturingProcessAdapterExt.kt index 73b346b..083318e 100644 --- a/src/main/kotlin/com/github/biomejs/intellijbiome/extensions/CapturingProcessAdapterExt.kt +++ b/src/main/kotlin/com/github/biomejs/intellijbiome/extensions/CapturingProcessAdapterExt.kt @@ -6,16 +6,15 @@ import com.intellij.execution.process.ProcessEvent import com.intellij.execution.process.ProcessOutput import java.util.concurrent.CompletableFuture -class ProcessResult(val processEvent: ProcessEvent, val processOutput: ProcessOutput) +class ProcessResult( val processOutput: ProcessOutput) -val ProcessEvent.isSuccess: Boolean get() = exitCode == 0 fun runProcessFuture(handler: OSProcessHandler): CompletableFuture { val future = CompletableFuture() handler.addProcessListener(object : CapturingProcessAdapter() { override fun processTerminated(event: ProcessEvent) { - future.complete(ProcessResult(event, output)) + future.complete(ProcessResult(output)) } }) diff --git a/src/main/kotlin/com/github/biomejs/intellijbiome/listeners/BiomeConfigListener.kt b/src/main/kotlin/com/github/biomejs/intellijbiome/listeners/BiomeConfigListener.kt index 2c225fb..7531d69 100644 --- a/src/main/kotlin/com/github/biomejs/intellijbiome/listeners/BiomeConfigListener.kt +++ b/src/main/kotlin/com/github/biomejs/intellijbiome/listeners/BiomeConfigListener.kt @@ -12,7 +12,8 @@ class BiomeConfigListener(val project: Project) : BulkFileListener { super.after(events) events.forEach { if (it.file?.name?.contains(BiomePackage.configName) == true && BiomePackage.configValidExtensions.contains( - it.file?.extension)) { + it.file?.extension) + ) { val biomeServerService = project.service() biomeServerService.restartBiomeServer() } diff --git a/src/main/kotlin/com/github/biomejs/intellijbiome/lsp/BiomeLspServerSupportProvider.kt b/src/main/kotlin/com/github/biomejs/intellijbiome/lsp/BiomeLspServerSupportProvider.kt index 055beaf..f51ab0a 100644 --- a/src/main/kotlin/com/github/biomejs/intellijbiome/lsp/BiomeLspServerSupportProvider.kt +++ b/src/main/kotlin/com/github/biomejs/intellijbiome/lsp/BiomeLspServerSupportProvider.kt @@ -11,13 +11,15 @@ import com.intellij.execution.process.OSProcessHandler import com.intellij.openapi.components.service import com.intellij.openapi.project.Project import com.intellij.openapi.vfs.VirtualFile -import com.intellij.platform.lsp.api.* +import com.intellij.platform.lsp.api.LspServer +import com.intellij.platform.lsp.api.LspServerSupportProvider +import com.intellij.platform.lsp.api.ProjectWideLspServerDescriptor import com.intellij.platform.lsp.api.customization.LspFormattingSupport import com.intellij.platform.lsp.api.lsWidget.LspServerWidgetItem import com.intellij.util.SmartList -@Suppress("UnstableApiUsage") -class BiomeLspServerSupportProvider : LspServerSupportProvider { + +@Suppress("UnstableApiUsage") class BiomeLspServerSupportProvider : LspServerSupportProvider { override fun fileOpened( project: Project, file: VirtualFile, @@ -40,15 +42,13 @@ class BiomeLspServerSupportProvider : LspServerSupportProvider { LspServerWidgetItem(lspServer, currentFile, BiomeIcons.BiomeIcon, BiomeConfigurable::class.java) } -@Suppress("UnstableApiUsage") -private class BiomeLspServerDescriptor(project: Project, +@Suppress("UnstableApiUsage") private class BiomeLspServerDescriptor(project: Project, val executable: String, - val configPath: String?) : ProjectWideLspServerDescriptor( - project, "Biome") { + val configPath: String?) : ProjectWideLspServerDescriptor(project, "Biome") { private val targetRunBuilder = BiomeTargetRunBuilder(project) override fun isSupportedFile(file: VirtualFile): Boolean { - return BiomeSettings.getInstance(project).fileSupported(project, file) + return BiomeSettings.getInstance(project).fileSupported(file) } override fun createCommandLine(): GeneralCommandLine { @@ -80,7 +80,7 @@ private class BiomeLspServerDescriptor(project: Project, serverExplicitlyWantsToFormatThisFile: Boolean, ): Boolean { val settings = BiomeSettings.getInstance(project) - return settings.enableLspFormat && BiomeSettings.getInstance(project).fileSupported(project, file) + return settings.enableLspFormat } } } diff --git a/src/main/kotlin/com/github/biomejs/intellijbiome/services/BiomeServerService.kt b/src/main/kotlin/com/github/biomejs/intellijbiome/services/BiomeServerService.kt index 65dabac..aed9ce5 100644 --- a/src/main/kotlin/com/github/biomejs/intellijbiome/services/BiomeServerService.kt +++ b/src/main/kotlin/com/github/biomejs/intellijbiome/services/BiomeServerService.kt @@ -1,24 +1,64 @@ +@file:Suppress("UnstableApiUsage") + package com.github.biomejs.intellijbiome.services import com.github.biomejs.intellijbiome.BiomeBundle import com.github.biomejs.intellijbiome.listeners.BiomeEditorPanelListener import com.github.biomejs.intellijbiome.lsp.BiomeLspServerSupportProvider +import com.intellij.codeStyle.AbstractConvertLineSeparatorsAction import com.intellij.notification.NotificationGroupManager import com.intellij.notification.NotificationType +import com.intellij.openapi.command.WriteCommandAction import com.intellij.openapi.components.Service +import com.intellij.openapi.editor.Document +import com.intellij.openapi.fileEditor.FileDocumentManager import com.intellij.openapi.fileEditor.FileEditorManagerListener import com.intellij.openapi.project.Project +import com.intellij.openapi.util.text.StringUtil +import com.intellij.openapi.vfs.VirtualFile import com.intellij.platform.lsp.api.LspServerManager +import com.intellij.platform.lsp.api.customization.LspIntentionAction +import com.intellij.platform.lsp.impl.LspServerImpl +import com.intellij.platform.lsp.util.getLsp4jRange +import com.intellij.util.LineSeparator +import org.eclipse.lsp4j.* +import java.util.* @Service(Service.Level.PROJECT) class BiomeServerService(private val project: Project) { private val editorPanelListener: BiomeEditorPanelListener + private val groupId = "Biome" + + enum class Feature { + Format, ApplySafeFixes, SortImports + } + init { editorPanelListener = BiomeEditorPanelListener(project) project.messageBus.connect().subscribe(FileEditorManagerListener.FILE_EDITOR_MANAGER, editorPanelListener) } + companion object { + fun getInstance(project: Project): BiomeServerService = project.getService(BiomeServerService::class.java) + } + + fun getServer(): LspServerImpl? = + LspServerManager.getInstance(project).getServersForProvider(BiomeLspServerSupportProvider::class.java) + .firstOrNull().let { it as? LspServerImpl } + + suspend fun applySafeFixes(document: Document) { + executeFeatures(document, EnumSet.of(Feature.ApplySafeFixes)) + } + + suspend fun sortImports(document: Document) { + executeFeatures(document, EnumSet.of(Feature.SortImports)) + } + + suspend fun format(document: Document) { + executeFeatures(document, EnumSet.of(Feature.Format)) + } + fun getCurrentConfigPath(): String? { return editorPanelListener.getCurrentConfigPath() } @@ -31,6 +71,106 @@ class BiomeServerService(private val project: Project) { LspServerManager.getInstance(project).stopServers(BiomeLspServerSupportProvider::class.java) } + suspend fun executeFeatures(document: Document, + features: EnumSet) { + val server = getServer() ?: return + val manager = FileDocumentManager.getInstance() + val file = manager.getFile(document) ?: return + val commandName = BiomeBundle.message("biome.run.biome.check.with.features", + features.joinToString(prefix = "(", postfix = ")") { it -> it.toString().lowercase() }) + + val formattingActions = mutableListOf() + + if (features.contains(Feature.ApplySafeFixes) || features.contains(Feature.SortImports)) { + val result = mutableListOf() + + if (features.contains(Feature.ApplySafeFixes)) { + val codeActionParams = CodeActionParams(server.getDocumentIdentifier(file), + getLsp4jRange(document, 0, document.textLength), + CodeActionContext().apply { + diagnostics = emptyList() + only = listOf("quickfix.biome") + triggerKind = CodeActionTriggerKind.Automatic + }) + + val codeActionResults = server.sendRequest { it.textDocumentService.codeAction(codeActionParams) } + codeActionResults?.forEach { + if (it.isRight) { + result.add(LspIntentionAction(server, it.right)) + } + } + } + + if (features.contains(Feature.SortImports)) { + val codeActionParams = CodeActionParams(server.getDocumentIdentifier(file), + getLsp4jRange(document, 0, document.textLength), + CodeActionContext().apply { + diagnostics = emptyList() + only = listOf("source.organizeImports.biome") + triggerKind = CodeActionTriggerKind.Automatic + }) + + val codeActionResults = server.sendRequest { it.textDocumentService.codeAction(codeActionParams) } + codeActionResults?.forEach { + if (it.isRight) { + result.add(LspIntentionAction(server, it.right)) + } + } + } + + WriteCommandAction.runWriteCommandAction(project, commandName, groupId, { + result.forEach { + if (it.isAvailable()) { + it.invoke(null) + } + } + }) + } + + if (features.contains(Feature.Format)) { + val formattingParams = + DocumentFormattingParams(server.getDocumentIdentifier(file), FormattingOptions().apply { + tabSize = 2 // Biome doesn't use this information + isInsertSpaces = false // Biome doesn't use this information + }) + + val formattingResults = server.sendRequest { it.textDocumentService.formatting(formattingParams) } + + formattingResults?.forEach { + val lineSeparator = StringUtil.detectSeparators(it.newText) + val normalizedText = StringUtil.convertLineSeparators(it.newText) + + formattingActions.add(Runnable { + if (!StringUtil.equals(document.charsSequence, normalizedText)) { + document.setText(normalizedText) + } + + setDetectedLineSeparator(project, file, lineSeparator) + }) + } + + WriteCommandAction.runWriteCommandAction(project, commandName, groupId, { + formattingActions.forEach { it.run() } + }) + } + } + + private fun setDetectedLineSeparator( + project: Project, + vFile: VirtualFile, + newSeparator: LineSeparator?, + ): Boolean { + if (newSeparator != null) { + val newSeparatorString: String = newSeparator.separatorString + + if (!StringUtil.equals(vFile.detectedLineSeparator, newSeparatorString)) { + AbstractConvertLineSeparatorsAction.changeLineSeparators(project, vFile, newSeparatorString) + return true + } + } + return false + } + fun notifyRestart() { NotificationGroupManager.getInstance() .getNotificationGroup("Biome") diff --git a/src/main/kotlin/com/github/biomejs/intellijbiome/settings/BiomeActionInfo.kt b/src/main/kotlin/com/github/biomejs/intellijbiome/settings/BiomeActionInfo.kt index 42e02ae..023ba67 100644 --- a/src/main/kotlin/com/github/biomejs/intellijbiome/settings/BiomeActionInfo.kt +++ b/src/main/kotlin/com/github/biomejs/intellijbiome/settings/BiomeActionInfo.kt @@ -8,7 +8,6 @@ class ActionInfo { companion object { fun defaultComment( version: String?, - filePattern: String, isActionOnSaveEnabled: Boolean ): ActionOnSaveComment? { if (version == null) { @@ -22,11 +21,11 @@ class ActionInfo { BiomeBundle.message( "biome.run.on.save.version.and.files.pattern", shorten(version, 15), - shorten(filePattern, 40) ) ) } - fun shorten(s: String, max: Int) = StringUtil.shortenTextWithEllipsis(s, max, 0, true) + fun shorten(s: String, + max: Int) = StringUtil.shortenTextWithEllipsis(s, max, 0, true) } } diff --git a/src/main/kotlin/com/github/biomejs/intellijbiome/settings/BiomeConfigurable.kt b/src/main/kotlin/com/github/biomejs/intellijbiome/settings/BiomeConfigurable.kt index 1fd97b6..52efecc 100644 --- a/src/main/kotlin/com/github/biomejs/intellijbiome/settings/BiomeConfigurable.kt +++ b/src/main/kotlin/com/github/biomejs/intellijbiome/settings/BiomeConfigurable.kt @@ -5,11 +5,8 @@ import com.github.biomejs.intellijbiome.BiomePackage import com.github.biomejs.intellijbiome.services.BiomeServerService import com.intellij.ide.actionsOnSave.ActionsOnSaveConfigurable import com.intellij.lang.javascript.JavaScriptBundle -import com.intellij.openapi.Disposable import com.intellij.openapi.application.ApplicationNamesInfo import com.intellij.openapi.components.service -import com.intellij.openapi.observable.properties.ObservableMutableProperty -import com.intellij.openapi.observable.util.whenItemSelected import com.intellij.openapi.options.BoundSearchableConfigurable import com.intellij.openapi.project.Project import com.intellij.openapi.ui.DialogPanel @@ -17,7 +14,6 @@ import com.intellij.openapi.ui.TextFieldWithBrowseButton import com.intellij.openapi.ui.ValidationInfo import com.intellij.openapi.vfs.VirtualFile import com.intellij.ui.ContextHelpLabel -import com.intellij.ui.components.JBRadioButton import com.intellij.ui.components.JBTextField import com.intellij.ui.dsl.builder.* import com.intellij.ui.layout.ValidationInfoBuilder @@ -25,31 +21,26 @@ import com.intellij.ui.layout.not import com.intellij.ui.layout.selected import com.intellij.util.ui.JBUI import com.intellij.util.ui.UIUtil +import java.awt.event.ItemEvent import java.io.File -import java.nio.file.FileSystems -import java.util.regex.PatternSyntaxException import javax.swing.JCheckBox import javax.swing.JRadioButton -import javax.swing.text.JTextComponent +import javax.swing.event.HyperlinkEvent private const val HELP_TOPIC = "reference.settings.biome" class BiomeConfigurable(internal val project: Project) : - BoundSearchableConfigurable( - BiomeBundle.message("biome.settings.name"), - HELP_TOPIC, - CONFIGURABLE_ID - ) { + BoundSearchableConfigurable(BiomeBundle.message("biome.settings.name"), HELP_TOPIC, CONFIGURABLE_ID) { lateinit var runFormatOnSaveCheckBox: JCheckBox lateinit var enableLspFormatCheckBox: JCheckBox lateinit var runSafeFixesOnSaveCheckBox: JCheckBox - lateinit var runUnsafeFixesOnSaveCheckBox: JCheckBox - - lateinit var runForFilesField: JBTextField + lateinit var sortImportOnSaveCheckBox: JCheckBox lateinit var disabledConfiguration: JRadioButton private lateinit var automaticConfiguration: JRadioButton private lateinit var manualConfiguration: JRadioButton + private lateinit var extensionsField: JBTextField + override fun createPanel(): DialogPanel { val settings: BiomeSettings = BiomeSettings.getInstance(project) val biomeServerService = project.service() @@ -57,37 +48,34 @@ class BiomeConfigurable(internal val project: Project) : // ********************* // Configuration mode row // ********************* - return panel { buttonsGroup { row { disabledConfiguration = - radioButton( - JavaScriptBundle.message( - "settings.javascript.linters.autodetect.disabled", - displayName - ) - ) - .bindSelected(ConfigurationModeProperty(settings, ConfigurationMode.DISABLED)) - .component + radioButton(JavaScriptBundle.message("settings.javascript.linters.autodetect.disabled", + displayName)).bindSelected(ConfigurationModeProperty(settings, + ConfigurationMode.DISABLED)).component.apply { + addItemListener { e -> + if (e.stateChange == ItemEvent.SELECTED) { + runFormatOnSaveCheckBox.isSelected = false + enableLspFormatCheckBox.isSelected = false + runSafeFixesOnSaveCheckBox.isSelected = false + sortImportOnSaveCheckBox.isSelected = false + } + } + } } row { automaticConfiguration = - radioButton( - JavaScriptBundle.message( - "settings.javascript.linters.autodetect.configure.automatically", - displayName - ) - ) - .bindSelected(ConfigurationModeProperty(settings, ConfigurationMode.AUTOMATIC)) - .component - - val detectAutomaticallyHelpText = JavaScriptBundle.message( - "settings.javascript.linters.autodetect.configure.automatically.help.text", - ApplicationNamesInfo.getInstance().fullProductName, - displayName, - "${BiomePackage.configName}.json" - ) + radioButton(JavaScriptBundle.message("settings.javascript.linters.autodetect.configure.automatically", + displayName)).bindSelected(ConfigurationModeProperty(settings, + ConfigurationMode.AUTOMATIC)).component + + val detectAutomaticallyHelpText = + JavaScriptBundle.message("settings.javascript.linters.autodetect.configure.automatically.help.text", + ApplicationNamesInfo.getInstance().fullProductName, + displayName, + "${BiomePackage.configName}.json") val helpLabel = ContextHelpLabel.create(detectAutomaticallyHelpText) helpLabel.border = JBUI.Borders.emptyLeft(UIUtil.DEFAULT_HGAP) @@ -95,14 +83,9 @@ class BiomeConfigurable(internal val project: Project) : } row { manualConfiguration = - radioButton( - JavaScriptBundle.message( - "settings.javascript.linters.autodetect.configure.manually", - displayName - ) - ) - .bindSelected(ConfigurationModeProperty(settings, ConfigurationMode.MANUAL)) - .component + radioButton(JavaScriptBundle.message("settings.javascript.linters.autodetect.configure.manually", + displayName)).bindSelected(ConfigurationModeProperty(settings, + ConfigurationMode.MANUAL)).component } } @@ -111,46 +94,58 @@ class BiomeConfigurable(internal val project: Project) : // ********************* panel { row(BiomeBundle.message("biome.path.executable")) { - textFieldWithBrowseButton(BiomeBundle.message("biome.path.executable")) { fileChosen(it) } - .bindText(settings::executablePath) + textFieldWithBrowseButton(BiomeBundle.message("biome.path.executable")) { fileChosen(it) }.bindText( + settings::executablePath) }.visibleIf(manualConfiguration.selected) row(BiomeBundle.message("biome.config.path.label")) { textFieldWithBrowseButton( BiomeBundle.message("biome.config.path.label"), project, - ) { fileChosen(it) } - .bindText(settings::configPath) - .validationOnInput(validateConfigDir()) + ) { fileChosen(it) }.bindText(settings::configPath).validationOnInput(validateConfigDir()) }.visibleIf(manualConfiguration.selected) } // ********************* - // Format pattern row + // Supported file extensions row // ********************* - row(BiomeBundle.message("biome.run.format.for.files.label")) { - runForFilesField = textField() + row(BiomeBundle.message("biome.supported.extensions.label")) { + extensionsField = textField() .align(AlignX.FILL) - .bind( - { textField -> textField.text.trim() }, - JTextComponent::setText, - MutableProperty({ settings.filePattern }, { settings.filePattern = it }) - ) - .validationOnInput(validateGlob()) + .bindText({ settings.supportedExtensions.joinToString(",") }, { value -> + settings.supportedExtensions = + value.split(",").map { it.trim() }.filter { it.isNotBlank() }.toMutableList() + }) + .validationOnInput { validateExtensions(it) } + .applyToComponent { + font = font.deriveFont(font.size2D - 2f) // Reduce font size by 2 points + } .component + }.enabledIf(!disabledConfiguration.selected) + // Add help text with a "Reset" link below the field + row { + comment(BiomeBundle.message("biome.supported.extensions.comment") + " Reset to Defaults") + .applyToComponent { + addHyperlinkListener { event -> + if (event.eventType == HyperlinkEvent.EventType.ACTIVATED && event.description == "reset") { + extensionsField.text = BiomeSettingsState.DEFAULT_EXTENSION_LIST.joinToString(",") + } + } + } + } + .bottomGap(BottomGap.MEDIUM) + .enabledIf(!disabledConfiguration.selected) + // ********************* // LSP row // ********************* row { - enableLspFormatCheckBox = checkBox(BiomeBundle.message("biome.enable.lsp.format.label")) - .bindSelected(RunOnObservableProperty( - { settings.configurationMode != ConfigurationMode.DISABLED && settings.enableLspFormat }, - { settings.enableLspFormat = it }, - { !disabledConfiguration.isSelected && enableLspFormatCheckBox.isSelected } - )) - .component + enableLspFormatCheckBox = checkBox(BiomeBundle.message("biome.enable.lsp.format.label")).bindSelected( + { settings.configurationMode != ConfigurationMode.DISABLED && settings.enableLspFormat }, + { settings.enableLspFormat = it }, + ).component val helpLabel = ContextHelpLabel.create(BiomeBundle.message("biome.enable.lsp.format.help.label")) helpLabel.border = JBUI.Borders.emptyLeft(UIUtil.DEFAULT_HGAP) @@ -164,13 +159,10 @@ class BiomeConfigurable(internal val project: Project) : // Format on save row // ********************* row { - runFormatOnSaveCheckBox = checkBox(BiomeBundle.message("biome.run.format.on.save.label")) - .bindSelected(RunOnObservableProperty( - { settings.configurationMode != ConfigurationMode.DISABLED && settings.formatOnSave }, - { settings.formatOnSave = it }, - { !disabledConfiguration.isSelected && runFormatOnSaveCheckBox.isSelected } - )) - .component + runFormatOnSaveCheckBox = checkBox(BiomeBundle.message("biome.run.format.on.save.label")).bindSelected( + { settings.configurationMode != ConfigurationMode.DISABLED && settings.formatOnSave }, + { settings.formatOnSave = it }, + ).component val link = ActionsOnSaveConfigurable.createGoToActionsOnSavePageLink() cell(link) @@ -180,13 +172,11 @@ class BiomeConfigurable(internal val project: Project) : // Apply safe fixes on save row // ********************* row { - runSafeFixesOnSaveCheckBox = checkBox(BiomeBundle.message("biome.run.safe.fixes.on.save.label")) - .bindSelected(RunOnObservableProperty( + runSafeFixesOnSaveCheckBox = + checkBox(BiomeBundle.message("biome.run.safe.fixes.on.save.label")).bindSelected( { settings.configurationMode != ConfigurationMode.DISABLED && settings.applySafeFixesOnSave }, { settings.applySafeFixesOnSave = it }, - { !disabledConfiguration.isSelected && runSafeFixesOnSaveCheckBox.isSelected } - )) - .component + ).component val link = ActionsOnSaveConfigurable.createGoToActionsOnSavePageLink() cell(link) @@ -194,16 +184,14 @@ class BiomeConfigurable(internal val project: Project) : // ********************* - // Apply unsafe fixes on save row + // Sort import on save row // ********************* row { - runUnsafeFixesOnSaveCheckBox = checkBox(BiomeBundle.message("biome.run.unsafe.fixes.on.save.label")) - .bindSelected(RunOnObservableProperty( - { settings.configurationMode != ConfigurationMode.DISABLED && settings.applyUnsafeFixesOnSave }, - { settings.applyUnsafeFixesOnSave = it }, - { !disabledConfiguration.isSelected && runUnsafeFixesOnSaveCheckBox.isSelected } - )) - .component + sortImportOnSaveCheckBox = + checkBox(BiomeBundle.message("biome.sort.import.on.save.label")).bindSelected( + { settings.configurationMode != ConfigurationMode.DISABLED && settings.sortImportOnSave }, + { settings.sortImportOnSave = it }, + ).component val link = ActionsOnSaveConfigurable.createGoToActionsOnSavePageLink() cell(link) @@ -214,39 +202,34 @@ class BiomeConfigurable(internal val project: Project) : biomeServerService.notifyRestart() } } - } - private fun validateGlob(): ValidationInfoBuilder.(JBTextField) -> ValidationInfo? = - { - try { - FileSystems.getDefault().getPathMatcher("glob:" + it.text) - null - } - catch (e: PatternSyntaxException) { - ValidationInfo(BiomeBundle.message("biome.invalid.pattern"), it) - } + private fun validateExtensions(field: JBTextField): ValidationInfo? { + val input = field.text + val extensions = input.split(",").map { it.trim() } + + val invalidExtension = extensions.find { !it.matches(Regex("^\\.[a-zA-Z0-9]+$")) } + return if (invalidExtension != null) { + ValidationInfo("Invalid extension: $invalidExtension. Must start with '.' and contain only alphanumeric characters.", + field) + } else { + null } + } - private fun validateConfigDir(): ValidationInfoBuilder.(TextFieldWithBrowseButton) -> ValidationInfo? = - { - val selected = File(it.text) + private fun validateConfigDir(): ValidationInfoBuilder.(TextFieldWithBrowseButton) -> ValidationInfo? = { + val selected = File(it.text) - if (!selected.exists()) { + if (!selected.exists()) { + ValidationInfo(BiomeBundle.message("biome.configuration.file.not.found"), it) + } else { + if (!selected.name.contains(BiomePackage.configName) && BiomePackage.configValidExtensions.contains(selected.extension)) { ValidationInfo(BiomeBundle.message("biome.configuration.file.not.found"), it) - } - else { - if (!selected.name.contains(BiomePackage.configName) && BiomePackage.configValidExtensions.contains( - selected.extension - ) - ) { - ValidationInfo(BiomeBundle.message("biome.configuration.file.not.found"), it) - } - else { - null - } + } else { + null } } + } private fun fileChosen(file: VirtualFile): String { return file.path @@ -256,8 +239,7 @@ class BiomeConfigurable(internal val project: Project) : private val settings: BiomeSettings, private val mode: ConfigurationMode, ) : MutableProperty { - override fun get(): Boolean = - settings.configurationMode == mode + override fun get(): Boolean = settings.configurationMode == mode override fun set(value: Boolean) { if (value) { @@ -266,31 +248,6 @@ class BiomeConfigurable(internal val project: Project) : } } - private inner class RunOnObservableProperty( - private val getter: () -> Boolean, - private val setter: (Boolean) -> Unit, - private val afterConfigModeChangeGetter: () -> Boolean, - ) : ObservableMutableProperty { - override fun set(value: Boolean) { - setter(value) - } - - override fun get(): Boolean = - getter() - - override fun afterChange(parentDisposable: Disposable?, listener: (Boolean) -> Unit) { - fun emitChange(radio: JBRadioButton) { - if (radio.isSelected) { - listener(afterConfigModeChangeGetter()) - } - } - - manualConfiguration.whenItemSelected(parentDisposable, ::emitChange) - automaticConfiguration.whenItemSelected(parentDisposable, ::emitChange) - disabledConfiguration.whenItemSelected(parentDisposable, ::emitChange) - } - } - companion object { const val CONFIGURABLE_ID = "Settings.Biome" } diff --git a/src/main/kotlin/com/github/biomejs/intellijbiome/settings/BiomeOnSaveApplySafeFixesActionInfo.kt b/src/main/kotlin/com/github/biomejs/intellijbiome/settings/BiomeOnSaveApplySafeFixesActionInfo.kt index fce365f..833acb9 100644 --- a/src/main/kotlin/com/github/biomejs/intellijbiome/settings/BiomeOnSaveApplySafeFixesActionInfo.kt +++ b/src/main/kotlin/com/github/biomejs/intellijbiome/settings/BiomeOnSaveApplySafeFixesActionInfo.kt @@ -8,14 +8,12 @@ import com.intellij.ide.actionsOnSave.ActionOnSaveContext import com.intellij.platform.ide.progress.runWithModalProgressBlocking class BiomeOnSaveApplySafeFixesActionInfo(actionOnSaveContext: ActionOnSaveContext) : - ActionOnSaveBackedByOwnConfigurable( - actionOnSaveContext, + ActionOnSaveBackedByOwnConfigurable(actionOnSaveContext, BiomeConfigurable.CONFIGURABLE_ID, - BiomeConfigurable::class.java - ) { + BiomeConfigurable::class.java) { override fun getActionOnSaveName() = - BiomeBundle.message("biome.run.safe.fixes.on.save.checkbox.on.actions.on.save.page") + BiomeBundle.message("biome.apply.safe.fixes.on.save.checkbox.on.actions.on.save.page") override fun isApplicableAccordingToStoredState(): Boolean = BiomeSettings.getInstance(project).configurationMode != ConfigurationMode.DISABLED @@ -28,30 +26,28 @@ class BiomeOnSaveApplySafeFixesActionInfo(actionOnSaveContext: ActionOnSaveConte override fun isActionOnSaveEnabledAccordingToUiState(configurable: BiomeConfigurable) = configurable.runSafeFixesOnSaveCheckBox.isSelected - override fun setActionOnSaveEnabled(configurable: BiomeConfigurable, enabled: Boolean) { + override fun setActionOnSaveEnabled(configurable: BiomeConfigurable, + enabled: Boolean) { configurable.runSafeFixesOnSaveCheckBox.isSelected = enabled } override fun getCommentAccordingToUiState(configurable: BiomeConfigurable): ActionOnSaveComment? { - if (!isSaveActionApplicable) return ActionOnSaveComment.info(BiomeBundle.message("biome.on.save.comment.disabled")) - - val biomePackage = BiomePackage(project) - val version = runWithModalProgressBlocking(project, BiomeBundle.message("biome.version")) { - biomePackage.versionNumber() - } - return ActionInfo.defaultComment(version, configurable.runForFilesField.text.trim(), isActionOnSaveEnabled) + return comment() } override fun getCommentAccordingToStoredState(): ActionOnSaveComment? { + return comment() + } + + override fun getActionLinks() = listOf(createGoToPageInSettingsLink(BiomeConfigurable.CONFIGURABLE_ID)) + + private fun comment(): ActionOnSaveComment? { if (!isSaveActionApplicable) return ActionOnSaveComment.info(BiomeBundle.message("biome.on.save.comment.disabled")) val biomePackage = BiomePackage(project) - val settings = BiomeSettings.getInstance(project) val version = runWithModalProgressBlocking(project, BiomeBundle.message("biome.version")) { biomePackage.versionNumber() } - return ActionInfo.defaultComment(version, settings.filePattern, isActionOnSaveEnabled) + return ActionInfo.defaultComment(version, isActionOnSaveEnabled) } - - override fun getActionLinks() = listOf(createGoToPageInSettingsLink(BiomeConfigurable.CONFIGURABLE_ID)) } diff --git a/src/main/kotlin/com/github/biomejs/intellijbiome/settings/BiomeOnSaveFormatActionInfo.kt b/src/main/kotlin/com/github/biomejs/intellijbiome/settings/BiomeOnSaveFormatActionInfo.kt index 9df03fc..f9f580f 100644 --- a/src/main/kotlin/com/github/biomejs/intellijbiome/settings/BiomeOnSaveFormatActionInfo.kt +++ b/src/main/kotlin/com/github/biomejs/intellijbiome/settings/BiomeOnSaveFormatActionInfo.kt @@ -15,7 +15,7 @@ class BiomeOnSaveFormatActionInfo(actionOnSaveContext: ActionOnSaveContext) : ) { override fun getActionOnSaveName() = - BiomeBundle.message("biome.run.format.on.save.checkbox.on.actions.on.save.page") + BiomeBundle.message("biome.format.on.save.checkbox.on.actions.on.save.page") override fun isApplicableAccordingToStoredState(): Boolean = BiomeSettings.getInstance(project).configurationMode != ConfigurationMode.DISABLED @@ -28,31 +28,29 @@ class BiomeOnSaveFormatActionInfo(actionOnSaveContext: ActionOnSaveContext) : override fun isActionOnSaveEnabledAccordingToUiState(configurable: BiomeConfigurable) = configurable.runFormatOnSaveCheckBox.isSelected - override fun setActionOnSaveEnabled(configurable: BiomeConfigurable, enabled: Boolean) { + override fun setActionOnSaveEnabled(configurable: BiomeConfigurable, + enabled: Boolean) { configurable.runFormatOnSaveCheckBox.isSelected = enabled } override fun getActionLinks() = listOf(createGoToPageInSettingsLink(BiomeConfigurable.CONFIGURABLE_ID)) override fun getCommentAccordingToUiState(configurable: BiomeConfigurable): ActionOnSaveComment? { - if (!isSaveActionApplicable) return ActionOnSaveComment.info(BiomeBundle.message("biome.on.save.comment.disabled")) - - val biomePackage = BiomePackage(project) - val version = runWithModalProgressBlocking(project, BiomeBundle.message("biome.version")) { - biomePackage.versionNumber() - } - return ActionInfo.defaultComment(version, configurable.runForFilesField.text.trim(), isActionOnSaveEnabled) + return comment() } override fun getCommentAccordingToStoredState(): ActionOnSaveComment? { + return comment() + } + + private fun comment(): ActionOnSaveComment? { if (!isSaveActionApplicable) return ActionOnSaveComment.info(BiomeBundle.message("biome.on.save.comment.disabled")) val biomePackage = BiomePackage(project) - val settings = BiomeSettings.getInstance(project) val version = runWithModalProgressBlocking(project, BiomeBundle.message("biome.version")) { biomePackage.versionNumber() } - return ActionInfo.defaultComment(version, settings.filePattern, isActionOnSaveEnabled) + return ActionInfo.defaultComment(version, isActionOnSaveEnabled) } } diff --git a/src/main/kotlin/com/github/biomejs/intellijbiome/settings/BiomeOnSaveInfoProvider.kt b/src/main/kotlin/com/github/biomejs/intellijbiome/settings/BiomeOnSaveInfoProvider.kt index a3a7045..19d684f 100644 --- a/src/main/kotlin/com/github/biomejs/intellijbiome/settings/BiomeOnSaveInfoProvider.kt +++ b/src/main/kotlin/com/github/biomejs/intellijbiome/settings/BiomeOnSaveInfoProvider.kt @@ -6,19 +6,15 @@ import com.intellij.ide.actionsOnSave.ActionOnSaveInfo import com.intellij.ide.actionsOnSave.ActionOnSaveInfoProvider class BiomeOnSaveInfoProvider : ActionOnSaveInfoProvider() { - override fun getActionOnSaveInfos(context: ActionOnSaveContext): - List = listOf( + override fun getActionOnSaveInfos(context: ActionOnSaveContext): List = listOf( BiomeOnSaveFormatActionInfo(context), BiomeOnSaveApplySafeFixesActionInfo(context), - BiomeOnSaveApplyUnsafeFixesActionInfo(context) - ) + BiomeOnSaveSortImportActionInfo(context)) override fun getSearchableOptions(): Collection { - return listOf( - BiomeBundle.message("biome.run.format.on.save.checkbox.on.actions.on.save.page"), - BiomeBundle.message("biome.run.safe.fixes.on.save.checkbox.on.actions.on.save.page"), - BiomeBundle.message("biome.run.unsafe.fixes.on.save.checkbox.on.actions.on.save.page") - ) + return listOf(BiomeBundle.message("biome.format.on.save.checkbox.on.actions.on.save.page"), + BiomeBundle.message("biome.apply.safe.fixes.on.save.checkbox.on.actions.on.save.page"), + BiomeBundle.message("biome.run.sort.import.on.save.checkbox.on.actions.on.save.page")) } } diff --git a/src/main/kotlin/com/github/biomejs/intellijbiome/settings/BiomeOnSaveApplyUnsafeFixesActionInfo.kt b/src/main/kotlin/com/github/biomejs/intellijbiome/settings/BiomeOnSaveSortImportActionInfo.kt similarity index 65% rename from src/main/kotlin/com/github/biomejs/intellijbiome/settings/BiomeOnSaveApplyUnsafeFixesActionInfo.kt rename to src/main/kotlin/com/github/biomejs/intellijbiome/settings/BiomeOnSaveSortImportActionInfo.kt index 0f36c1a..96a89da 100644 --- a/src/main/kotlin/com/github/biomejs/intellijbiome/settings/BiomeOnSaveApplyUnsafeFixesActionInfo.kt +++ b/src/main/kotlin/com/github/biomejs/intellijbiome/settings/BiomeOnSaveSortImportActionInfo.kt @@ -7,7 +7,7 @@ import com.intellij.ide.actionsOnSave.ActionOnSaveComment import com.intellij.ide.actionsOnSave.ActionOnSaveContext import com.intellij.platform.ide.progress.runWithModalProgressBlocking -class BiomeOnSaveApplyUnsafeFixesActionInfo(actionOnSaveContext: ActionOnSaveContext) : +class BiomeOnSaveSortImportActionInfo(actionOnSaveContext: ActionOnSaveContext) : ActionOnSaveBackedByOwnConfigurable( actionOnSaveContext, BiomeConfigurable.CONFIGURABLE_ID, @@ -15,7 +15,7 @@ class BiomeOnSaveApplyUnsafeFixesActionInfo(actionOnSaveContext: ActionOnSaveCon ) { override fun getActionOnSaveName() = - BiomeBundle.message("biome.run.unsafe.fixes.on.save.checkbox.on.actions.on.save.page") + BiomeBundle.message("biome.run.sort.import.on.save.checkbox.on.actions.on.save.page") override fun isApplicableAccordingToStoredState(): Boolean = BiomeSettings.getInstance(project).configurationMode != ConfigurationMode.DISABLED @@ -24,36 +24,33 @@ class BiomeOnSaveApplyUnsafeFixesActionInfo(actionOnSaveContext: ActionOnSaveCon !configurable.disabledConfiguration.isSelected override fun isActionOnSaveEnabledAccordingToStoredState() = - BiomeSettings.getInstance(project).applyUnsafeFixesOnSave + BiomeSettings.getInstance(project).sortImportOnSave override fun isActionOnSaveEnabledAccordingToUiState(configurable: BiomeConfigurable) = - configurable.runUnsafeFixesOnSaveCheckBox.isSelected + configurable.sortImportOnSaveCheckBox.isSelected override fun setActionOnSaveEnabled(configurable: BiomeConfigurable, enabled: Boolean) { - configurable.runUnsafeFixesOnSaveCheckBox.isSelected = enabled + configurable.sortImportOnSaveCheckBox.isSelected = enabled } override fun getCommentAccordingToUiState(configurable: BiomeConfigurable): ActionOnSaveComment? { - if (!isSaveActionApplicable) return ActionOnSaveComment.info(BiomeBundle.message("biome.on.save.comment.disabled")) - - val biomePackage = BiomePackage(project) - val version = runWithModalProgressBlocking(project, BiomeBundle.message("biome.version")) { - biomePackage.versionNumber() - } - return ActionInfo.defaultComment(version, configurable.runForFilesField.text.trim(), isActionOnSaveEnabled) + return comment() } override fun getCommentAccordingToStoredState(): ActionOnSaveComment? { + return comment() + } + + override fun getActionLinks() = listOf(createGoToPageInSettingsLink(BiomeConfigurable.CONFIGURABLE_ID)) + + private fun comment(): ActionOnSaveComment? { if (!isSaveActionApplicable) return ActionOnSaveComment.info(BiomeBundle.message("biome.on.save.comment.disabled")) val biomePackage = BiomePackage(project) - val settings = BiomeSettings.getInstance(project) val version = runWithModalProgressBlocking(project, BiomeBundle.message("biome.version")) { biomePackage.versionNumber() } - return ActionInfo.defaultComment(version, settings.filePattern, isActionOnSaveEnabled) + return ActionInfo.defaultComment(version, isActionOnSaveEnabled) } - - override fun getActionLinks() = listOf(createGoToPageInSettingsLink(BiomeConfigurable.CONFIGURABLE_ID)) } diff --git a/src/main/kotlin/com/github/biomejs/intellijbiome/settings/BiomeSettings.kt b/src/main/kotlin/com/github/biomejs/intellijbiome/settings/BiomeSettings.kt index 95f528e..a7fc807 100644 --- a/src/main/kotlin/com/github/biomejs/intellijbiome/settings/BiomeSettings.kt +++ b/src/main/kotlin/com/github/biomejs/intellijbiome/settings/BiomeSettings.kt @@ -1,7 +1,7 @@ package com.github.biomejs.intellijbiome.settings -import com.github.biomejs.intellijbiome.Feature -import com.intellij.lang.javascript.linter.GlobPatternUtil +import com.github.biomejs.intellijbiome.services.BiomeServerService.Feature +import com.github.biomejs.intellijbiome.settings.BiomeSettingsState.Companion.DEFAULT_EXTENSION_LIST import com.intellij.openapi.components.* import com.intellij.openapi.project.Project import com.intellij.openapi.vfs.VirtualFile @@ -29,10 +29,11 @@ class BiomeSettings : state.configPath = value } - var filePattern: String - get() = state.filePattern ?: BiomeSettingsState.DEFAULT_FILE_PATTERN + var supportedExtensions: MutableList + get() = state.supportedExtensions.takeIf { it.isNotEmpty() } ?: DEFAULT_EXTENSION_LIST.toMutableList() set(value) { - state.filePattern = value + state.supportedExtensions.clear() + state.supportedExtensions.addAll(value) } var configurationMode: ConfigurationMode @@ -53,16 +54,16 @@ class BiomeSettings : state.formatOnSave = value } - var applySafeFixesOnSave: Boolean - get() = isEnabled() && state.applySafeFixesOnSave + var sortImportOnSave: Boolean + get() = isEnabled() && state.sortImportOnSave set(value) { - state.applySafeFixesOnSave = value + state.sortImportOnSave = value } - var applyUnsafeFixesOnSave: Boolean - get() = isEnabled() && state.applyUnsafeFixesOnSave + var applySafeFixesOnSave: Boolean + get() = isEnabled() && state.applySafeFixesOnSave set(value) { - state.applyUnsafeFixesOnSave = value + state.applySafeFixesOnSave = value } fun getEnabledFeatures(): EnumSet { @@ -71,10 +72,10 @@ class BiomeSettings : features.add(Feature.Format) } if (applySafeFixesOnSave) { - features.add(Feature.SafeFixes) + features.add(Feature.ApplySafeFixes) } - if (applyUnsafeFixesOnSave) { - features.add(Feature.UnsafeFixes) + if (sortImportOnSave) { + features.add(Feature.SortImports) } return features } @@ -83,9 +84,14 @@ class BiomeSettings : return configurationMode !== ConfigurationMode.DISABLED } - fun fileSupported(project: Project, - file: VirtualFile): Boolean = - GlobPatternUtil.isFileMatchingGlobPattern(project, filePattern, file) + fun fileSupported(file: VirtualFile): Boolean { + val fileExtension = file.extension + return if (fileExtension != null) { + supportedExtensions.contains(".$fileExtension") + } else { + false + } + } companion object { @JvmStatic diff --git a/src/main/kotlin/com/github/biomejs/intellijbiome/settings/BiomeSettingsState.kt b/src/main/kotlin/com/github/biomejs/intellijbiome/settings/BiomeSettingsState.kt index 85d3e11..1021921 100644 --- a/src/main/kotlin/com/github/biomejs/intellijbiome/settings/BiomeSettingsState.kt +++ b/src/main/kotlin/com/github/biomejs/intellijbiome/settings/BiomeSettingsState.kt @@ -16,14 +16,17 @@ enum class ConfigurationMode { class BiomeSettingsState : BaseState() { var executablePath by string() var configPath by string() - var filePattern by string(DEFAULT_FILE_PATTERN) var formatOnSave by property(false) var enableLspFormat by property(false) var applySafeFixesOnSave by property(false) - var applyUnsafeFixesOnSave by property(false) + var sortImportOnSave by property(false) var configurationMode by enum(ConfigurationMode.AUTOMATIC) + var supportedExtensions by list() companion object { - const val DEFAULT_FILE_PATTERN = "**/*.{js,mjs,cjs,ts,jsx,tsx,cts,json,jsonc,vue,svelte,astro,css}" + val DEFAULT_EXTENSION_LIST = listOf( + ".astro", ".css", ".gql", ".graphql", ".js", ".mjs", ".cjs", ".jsx", + ".json", ".jsonc", ".svelte", ".html", ".ts", ".mts", ".cts", ".tsx", ".vue" + ) } } diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index de5a228..1194d8e 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -63,7 +63,28 @@ + /> + + + + + + + + + + diff --git a/src/main/resources/messages/BiomeBundle.properties b/src/main/resources/messages/BiomeBundle.properties index 81655f7..cfc8994 100644 --- a/src/main/resources/messages/BiomeBundle.properties +++ b/src/main/resources/messages/BiomeBundle.properties @@ -12,14 +12,23 @@ biome.run.format.for.files.label=Run biome for &files: biome.enable.lsp.format.label=Enable LSP-based code formatting biome.run.safe.fixes.on.save.label=Run safe fixes on &save biome.run.format.on.save.label=Run format on &save -biome.run.unsafe.fixes.on.save.label=Run unsafe fixes on &save -biome.run.format.on.save.checkbox.on.actions.on.save.page=Run Biome check -biome.run.safe.fixes.on.save.checkbox.on.actions.on.save.page=Run Biome check with --apply -biome.run.unsafe.fixes.on.save.checkbox.on.actions.on.save.page=Run Biome check with --apply-unsafe -biome.run.biome.check.with.features=Running Biome check with {0} -biome.failed.to.run.biome.check.with.features=Failed to run Biome check with {0} for file {1} +biome.apply.safe.fixes.success.label=Safe fixes applied +biome.apply.safe.fixes.success.description=The safe fixes have been successfully applied +biome.apply.safe.fixes.failure.label=Failed to apply safe fixes +biome.apply.safe.fixes.failure.description=An error occurred while applying safe fixes: {0} +biome.apply.sort.import.success.label=Imports sorted +biome.apply.sort.import.success.description=The imports have been successfully sorted +biome.apply.sort.import.failure.label=Failed to sort imports +biome.apply.sort.import.failure.description=An error occurred while sorting imports: {0} +biome.apply.feature.on.save.failure.label=Failed to Execute Features: {0} +biome.apply.feature.on.save.failure.description=An error occurred while executing features {0}: {1} +biome.sort.import.on.save.label=Sort import on &save +biome.format.on.save.checkbox.on.actions.on.save.page=Run Biome format +biome.apply.safe.fixes.on.save.checkbox.on.actions.on.save.page=Run Biome apply safe fixes +biome.run.sort.import.on.save.checkbox.on.actions.on.save.page=Run Biome sort import +biome.run.biome.check.with.features=Running Biome with {0} biome.run.on.save.package.not.specified.warning=Biome package not specified -biome.run.on.save.version.and.files.pattern=Version {0}. Run for: {1} +biome.run.on.save.version.and.files.pattern=Version {0} biome.enable.lsp.format.help.label=If LSP formatting is enabled, you can use it as a Save Action in IntelliJ:
    \
  • Open Preferences/Settings in IntelliJ.
  • \
  • Navigate to Tools > Actions on Save.
  • \ @@ -28,5 +37,10 @@ biome.enable.lsp.format.help.label=If LSP formatting is enabled, you can u
\

This setup allows automatic LSP-based code formatting on save.

\

This feature also allows you to format code using the designated hotkey (Alt+Shift+Command+L on Mac or Ctrl+Alt+L on Windows/Linux).

+biome.supported.extensions.label=Supported extensions: +biome.supported.extensions.comment=Enter file extensions separated by commas (e.g., .js, .ts, .jsx). Each extension must start with a '.' and contain only alphanumeric characters. +biome.file.not.supported.title=File not supported +biome.file.not.supported.description=The file "{0}" is not supported by Biome. +biome.configure.extensions.link=Configure extensions diff --git a/src/test/kotlin/com/github/biomejs/intellijbiome/pages/Editor.kt b/src/test/kotlin/com/github/biomejs/intellijbiome/pages/Editor.kt index b109a8b..250bf00 100644 --- a/src/test/kotlin/com/github/biomejs/intellijbiome/pages/Editor.kt +++ b/src/test/kotlin/com/github/biomejs/intellijbiome/pages/Editor.kt @@ -9,7 +9,8 @@ import com.intellij.remoterobot.fixtures.FixtureName import com.intellij.remoterobot.search.locators.byXpath @JvmOverloads -fun ContainerFixture.editor(title: String, function: Editor.() -> Unit = {}): Editor { +fun ContainerFixture.editor(title: String, + function: Editor.() -> Unit = {}): Editor { find( byXpath("//div[@class='EditorTabs']//div[@accessiblename='$title' and @class='SimpleColoredComponent']"), ).click() diff --git a/src/test/kotlin/com/github/biomejs/intellijbiome/pages/IdeaFrame.kt b/src/test/kotlin/com/github/biomejs/intellijbiome/pages/IdeaFrame.kt index e5245a1..126275b 100644 --- a/src/test/kotlin/com/github/biomejs/intellijbiome/pages/IdeaFrame.kt +++ b/src/test/kotlin/com/github/biomejs/intellijbiome/pages/IdeaFrame.kt @@ -16,7 +16,8 @@ fun RemoteRobot.idea(function: IdeaFrame.() -> Unit) { @FixtureName("Idea frame") @DefaultXpath("IdeFrameImpl type", "//div[@class='IdeFrameImpl']") -class IdeaFrame(remoteRobot: RemoteRobot, remoteComponent: RemoteComponent) : +class IdeaFrame(remoteRobot: RemoteRobot, + remoteComponent: RemoteComponent) : CommonContainerFixture(remoteRobot, remoteComponent) { val menuBar: JMenuBarFixture get() = step("Menu...") { @@ -24,7 +25,8 @@ class IdeaFrame(remoteRobot: RemoteRobot, remoteComponent: RemoteComponent) : } @JvmOverloads - fun dumbAware(timeout: Duration = Duration.ofMinutes(5), function: () -> Unit) { + fun dumbAware(timeout: Duration = Duration.ofMinutes(5), + function: () -> Unit) { step("Wait for smart mode") { waitFor(duration = timeout, interval = Duration.ofSeconds(5)) { runCatching { isDumbMode().not() }.getOrDefault(false) diff --git a/src/test/kotlin/com/github/biomejs/intellijbiome/pages/StatusBar.kt b/src/test/kotlin/com/github/biomejs/intellijbiome/pages/StatusBar.kt index 99a646d..c953739 100644 --- a/src/test/kotlin/com/github/biomejs/intellijbiome/pages/StatusBar.kt +++ b/src/test/kotlin/com/github/biomejs/intellijbiome/pages/StatusBar.kt @@ -12,7 +12,8 @@ fun RemoteRobot.statusBar(function: StatusbarFrame.() -> Unit) { @FixtureName("Statusbar frame") @DefaultXpath("IdeStatusBarImpl type", "//div[@class='IdeStatusBarImpl']") -class StatusbarFrame(remoteRobot: RemoteRobot, remoteComponent: RemoteComponent) : +class StatusbarFrame(remoteRobot: RemoteRobot, + remoteComponent: RemoteComponent) : CommonContainerFixture(remoteRobot, remoteComponent) { val statusBarPanel diff --git a/src/test/kotlin/com/github/biomejs/intellijbiome/pages/WelcomeFrame.kt b/src/test/kotlin/com/github/biomejs/intellijbiome/pages/WelcomeFrame.kt index 0d55130..6e36362 100644 --- a/src/test/kotlin/com/github/biomejs/intellijbiome/pages/WelcomeFrame.kt +++ b/src/test/kotlin/com/github/biomejs/intellijbiome/pages/WelcomeFrame.kt @@ -15,7 +15,8 @@ fun RemoteRobot.welcomeFrame(function: WelcomeFrame.() -> Unit) { @FixtureName("Welcome Frame") @DefaultXpath("type", "//div[@class='FlatWelcomeFrame']") -class WelcomeFrame(remoteRobot: RemoteRobot, remoteComponent: RemoteComponent) : +class WelcomeFrame(remoteRobot: RemoteRobot, + remoteComponent: RemoteComponent) : CommonContainerFixture(remoteRobot, remoteComponent) { val openProjectLink get() = actionLink( diff --git a/src/test/kotlin/com/github/biomejs/intellijbiome/utils/RemoteRobotExtension.kt b/src/test/kotlin/com/github/biomejs/intellijbiome/utils/RemoteRobotExtension.kt index e9beec0..768c525 100644 --- a/src/test/kotlin/com/github/biomejs/intellijbiome/utils/RemoteRobotExtension.kt +++ b/src/test/kotlin/com/github/biomejs/intellijbiome/utils/RemoteRobotExtension.kt @@ -31,18 +31,20 @@ class RemoteRobotExtension : AfterTestExecutionCallback, ParameterResolver { } private val client = OkHttpClient() - override fun supportsParameter(parameterContext: ParameterContext?, extensionContext: ExtensionContext?): Boolean { - return parameterContext?.parameter?.type?.equals(RemoteRobot::class.java) ?: false + override fun supportsParameter(parameterContext: ParameterContext?, + extensionContext: ExtensionContext?): Boolean { + return parameterContext?.parameter?.type?.equals(RemoteRobot::class.java) == true } - override fun resolveParameter(parameterContext: ParameterContext?, extensionContext: ExtensionContext?): Any { + override fun resolveParameter(parameterContext: ParameterContext?, + extensionContext: ExtensionContext?): Any { return remoteRobot } override fun afterTestExecution(context: ExtensionContext?) { val testMethod: Method = context?.requiredTestMethod ?: throw IllegalStateException("test method is null") val testMethodName = testMethod.name - val testFailed: Boolean = context.executionException?.isPresent ?: false + val testFailed: Boolean = context.executionException?.isPresent == true if (testFailed) { // saveScreenshot(testMethodName) saveIdeaFrames(testMethodName) @@ -63,7 +65,9 @@ class RemoteRobotExtension : AfterTestExecutionCallback, ParameterResolver { println("Hierarchy snapshot: ${hierarchySnapshot.absolutePath}") } - private fun saveFile(url: String, folder: String, name: String): File { + private fun saveFile(url: String, + folder: String, + name: String): File { val response = client.newCall(Request.Builder().url(url).build()).execute() return File(folder).apply { mkdirs()