diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..9f51f5b
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,12 @@
+root = true
+
+[*]
+end_of_line = lf
+trim_trailing_whitespace = true
+insert_final_newline = true
+charset = utf-8
+indent_style = space
+indent_size = 2
+
+[*.kt]
+indent_size = 4
diff --git a/.github/workflows/integrate.yaml b/.github/workflows/integrate.yaml
index a3ffb03..351a6f3 100644
--- a/.github/workflows/integrate.yaml
+++ b/.github/workflows/integrate.yaml
@@ -2,11 +2,11 @@ name: Integrate
on:
push:
- branches: ["main"]
+ branches: [ "main" ]
pull_request:
workflow_dispatch:
jobs:
build:
name: Build
- uses: ./.github/workflows/_build.yaml
\ No newline at end of file
+ uses: ./.github/workflows/_build.yaml
diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml
index b8203c3..d5b761a 100644
--- a/.github/workflows/publish.yaml
+++ b/.github/workflows/publish.yaml
@@ -7,7 +7,7 @@ on:
type: boolean
default: true
required: false
- description: 'Publish a nightly build'
+ description: 'Publish a nightly build'
jobs:
build:
@@ -16,10 +16,10 @@ jobs:
with:
nightly: ${{ inputs.nightly }}
artifact: true
-
+
publish-jetbrains:
name: Publish to JetBrains Marketplace
- needs: [build]
+ needs: [ build ]
runs-on: ubuntu-latest
environment: intellij-plugin
steps:
@@ -32,7 +32,7 @@ jobs:
uses: actions/download-artifact@v4
with:
name: Biome-${{ needs.build.outputs.version }}.zip
-
+
- name: Restore gradle.properties
uses: actions/cache/restore@v3
with:
@@ -49,7 +49,7 @@ jobs:
publish-github-release:
name: Publish to GitHub Releases
- needs: [build]
+ needs: [ build ]
runs-on: ubuntu-latest
permissions:
contents: write
@@ -87,4 +87,4 @@ jobs:
prerelease: ${{ needs.build.outputs.nightly == 'true' }}
draft: true
files: biome.zip
- tag_name: ${{ needs.build.outputs.nightly == 'true' && github.ref || format('v{0}', needs.build.outputs.version) }}
\ No newline at end of file
+ tag_name: ${{ needs.build.outputs.nightly == 'true' && github.ref || format('v{0}', needs.build.outputs.version) }}
diff --git a/.gitignore b/.gitignore
index e2e5d94..4a170d1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,3 +2,5 @@
.idea
.qodana
build
+.DS_Store
+node_modules
diff --git a/.run/Run IDE for UI Tests.run.xml b/.run/Run IDE for UI Tests.run.xml
index ee99b7e..55343a1 100644
--- a/.run/Run IDE for UI Tests.run.xml
+++ b/.run/Run IDE for UI Tests.run.xml
@@ -1,25 +1,25 @@
-
+
-
-
-
-
+
+
+
+
-
+
true
true
false
false
-
+
-
\ No newline at end of file
+
diff --git a/.run/Run Plugin.run.xml b/.run/Run Plugin.run.xml
index d15ff68..03427a3 100644
--- a/.run/Run Plugin.run.xml
+++ b/.run/Run Plugin.run.xml
@@ -1,24 +1,24 @@
-
+
-
-
-
-
+
+
+
+
-
+
-
+
-
+
true
true
false
-
+
-
\ No newline at end of file
+
diff --git a/.run/Run Tests.run.xml b/.run/Run Tests.run.xml
index 132d9ad..6458c84 100644
--- a/.run/Run Tests.run.xml
+++ b/.run/Run Tests.run.xml
@@ -1,24 +1,24 @@
-
+
-
-
-
-
+
+
+
+
-
+
-
+
-
+
true
true
false
-
+
diff --git a/.run/Run Verifications.run.xml b/.run/Run Verifications.run.xml
index 3a8d688..bf8bccc 100644
--- a/.run/Run Verifications.run.xml
+++ b/.run/Run Verifications.run.xml
@@ -1,26 +1,27 @@
-
+
-
-
-
-
+
+
+
+
-
+
-
+
-
+
true
true
false
-
+
-
\ No newline at end of file
+
diff --git a/README.md b/README.md
index b304fa5..4a176bd 100644
--- a/README.md
+++ b/README.md
@@ -1,9 +1,12 @@
+[Biome](https://biomejs.dev/) is a powerful tool designed to enhance your development experience. This plugin integrates
+seamlessly with many [JetBrains IDE's](#Supported IDEs) to provide some capabilities:
-[Biome](https://biomejs.dev/) is a powerful tool designed to enhance your development experience. This plugin integrates seamlessly with many [JetBrains IDE's](#Supported IDEs) to provide some capabilities:
-
-- See lints while you type
-- Apply code fixes (from mouse-over, ⌥+⏎ or Alt+Enter)
-- Reformat your code with ⌥⇧+⌘+L or Ctrl+Alt+L (You can also format your [code on save](https://www.jetbrains.com/help/webstorm/reformat-and-rearrange-code.html#reformat-on-save))
+- See lints while you type
+- Apply code fixes (from mouse-over,⌥+⏎or
+ Alt+Enter)
+- Reformat your code with⌥⇧+⌘+Lor
+ Ctrl+Alt+L (You can also format
+ your [code on save](https://www.jetbrains.com/help/webstorm/reformat-and-rearrange-code.html#reformat-on-save))
However, please note the following limitations:
@@ -11,7 +14,8 @@ However, please note the following limitations:
## Installation
-To install the Biome IntelliJ Plugin, Head over to [official plugin page](https://plugins.jetbrains.com/plugin/22761-biome) or follow these steps:
+To install the Biome IntelliJ Plugin, Head over
+to [official plugin page](https://plugins.jetbrains.com/plugin/22761-biome) or follow these steps:
### From JetBrains IDEs
@@ -30,9 +34,11 @@ To install the Biome IntelliJ Plugin, Head over to [official plugin page](https:
## Biome Resolution
-The Plugin tries to use Biome from your project’s local dependencies (`node_modules/.bin/biome`). We recommend adding Biome as a project dependency to ensure that NPM scripts and the extension use the same Biome version.
+The Plugin tries to use Biome from your project’s local dependencies (`node_modules/.bin/biome`). We recommend adding
+Biome as a project dependency to ensure that NPM scripts and the extension use the same Biome version.
-You can also explicitly specify the `Biome` binary the extension should use by configuring the `Biome CLI Path` in `Settings`->`Language & Frameworks`->`Biome Settings`.
+You can also explicitly specify the`Biome`binary the extension should use by configuring the`Biome CLI Path`
+in `Settings`->`Language & Frameworks`->`Biome Settings`.
## Plugin settings
@@ -44,7 +50,7 @@ This setting overrides the Biome binary used by the plugin.
This plugin is currently supported in the following IDEs:
-- IntelliJ IDEA Ultimate >2023.2.2
+- IntelliJ IDEA Ultimate >2023.2.2
- WebStorm >2023.2.2
- AppCode >2023.2.2
- PhpStorm >2023.2.2
@@ -52,4 +58,7 @@ This plugin is currently supported in the following IDEs:
## Contributing
-We welcome contributions to the Biome IntelliJ Plugin. If you encounter any issues or have suggestions for improvements, please open an issue on our [GitHub repository](https://github.com/biomejs/biome/issues/new/choose). We also have a [Discord community](https://discord.gg/BypW39g6Yc) where you can discuss the plugin, ask questions, and connect with other Biome's developers.
+We welcome contributions to the Biome IntelliJ Plugin. If you encounter any issues or have suggestions for improvements,
+please open an issue on our [GitHub repository](https://github.com/biomejs/biome/issues/new/choose). We also have
+a [Discord community](https://discord.gg/BypW39g6Yc) where you can discuss the plugin, ask questions, and connect with
+other Biome's developers.
diff --git a/build.gradle.kts b/build.gradle.kts
index e8b9d48..65a3240 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -2,12 +2,12 @@ import java.net.URI
fun properties(key: String) = providers.gradleProperty(key)
fun environment(key: String) = providers.environmentVariable(key)
-val remoteRobotVersion = "0.11.20"
+val remoteRobotVersion = "0.11.21"
plugins {
- id("java") // Java support
- alias(libs.plugins.kotlin) // Kotlin support
- alias(libs.plugins.gradleIntelliJPlugin) // Gradle IntelliJ Plugin
+ id("java") // Java support
+ alias(libs.plugins.kotlin) // Kotlin support
+ alias(libs.plugins.gradleIntelliJPlugin) // Gradle IntelliJ Plugin
}
group = properties("pluginGroup").get()
@@ -15,88 +15,85 @@ version = properties("pluginVersion").get()
// Configure project's dependencies
repositories {
- mavenCentral()
- maven { url = URI("https://packages.jetbrains.team/maven/p/ij/intellij-dependencies") }
+ mavenCentral()
+ maven { url = URI("https://packages.jetbrains.team/maven/p/ij/intellij-dependencies") }
}
// Dependencies are managed with Gradle version catalog - read more: https://docs.gradle.org/current/userguide/platforms.html#sub:version-catalog
dependencies {
- testImplementation("com.intellij.remoterobot:remote-robot:$remoteRobotVersion")
- testImplementation("com.intellij.remoterobot:remote-fixtures:$remoteRobotVersion")
- testImplementation("org.junit.jupiter:junit-jupiter-api:5.9.3")
- testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.9.2")
- testRuntimeOnly("org.junit.platform:junit-platform-launcher:1.9.3")
-
- // Logging Network Calls
- testImplementation("com.squareup.okhttp3:logging-interceptor:4.11.0")
-
- // Video Recording
- implementation("com.automation-remarks:video-recorder-junit5:2.0")
+ testImplementation("com.intellij.remoterobot:remote-robot:$remoteRobotVersion")
+ testImplementation("com.intellij.remoterobot:remote-fixtures:$remoteRobotVersion")
+ testImplementation("org.junit.jupiter:junit-jupiter-api:5.10.0")
+ testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.10.0")
+ testRuntimeOnly("org.junit.platform:junit-platform-launcher:1.9.3")
+
+ // Logging Network Calls
+ testImplementation("com.squareup.okhttp3:logging-interceptor:4.12.0")
}
// Set the JVM language level used to build the project. Use Java 11 for 2020.3+, and Java 17 for 2022.2+.
kotlin {
- jvmToolchain(17)
+ jvmToolchain(17)
}
// Configure Gradle IntelliJ Plugin - read more: https://plugins.jetbrains.com/docs/intellij/tools-gradle-intellij-plugin.html
intellij {
- pluginName = properties("pluginName")
- version = properties("platformVersion")
- type = properties("platformType")
+ pluginName = properties("pluginName")
+ version = properties("platformVersion")
+ type = properties("platformType")
- // Plugin Dependencies. Uses `platformPlugins` property from the gradle.properties file.
- plugins = properties("platformPlugins").map { it.split(',').map(String::trim).filter(String::isNotEmpty) }
+ // Plugin Dependencies. Uses `platformPlugins` property from the gradle.properties file.
+ plugins = properties("platformPlugins").map { it.split(',').map(String::trim).filter(String::isNotEmpty) }
}
tasks {
- wrapper {
- gradleVersion = properties("gradleVersion").get()
- }
-
- patchPluginXml {
- version = properties("pluginVersion")
- sinceBuild = properties("pluginSinceBuild")
- untilBuild = properties("pluginUntilBuild")
- }
-
- downloadRobotServerPlugin {
- version.set(remoteRobotVersion)
- }
-
- // Configure UI tests plugin
- // Read more: https://github.com/JetBrains/intellij-ui-test-robot
- runIdeForUiTests {
- systemProperty("robot-server.port", "8082")
- systemProperty("ide.mac.message.dialogs.as.sheets", "false")
- systemProperty("jb.privacy.policy.text", "")
- systemProperty("jb.consents.confirmation.enabled", "false")
- systemProperty("ide.mac.file.chooser.native", "false")
- systemProperty("jbScreenMenuBar.enabled", "false")
- systemProperty("apple.laf.useScreenMenuBar", "false")
- systemProperty("idea.trust.all.projects", "true")
- systemProperty("ide.show.tips.on.startup.default.value", "false")
- systemProperty("eap.require.license", "false")
-
- }
-
- test {
- useJUnitPlatform()
- }
-
- signPlugin {
- certificateChain = environment("CERTIFICATE_CHAIN")
- privateKey = environment("PRIVATE_KEY")
- password = environment("PRIVATE_KEY_PASSWORD")
- }
-
- publishPlugin {
- token = environment("PUBLISH_TOKEN")
- // The pluginVersion is based on the SemVer (https://semver.org) and supports pre-release labels, like 2.1.7-alpha.3
- // Specify pre-release label to publish the plugin in a custom Release Channel automatically. Read more:
- // https://plugins.jetbrains.com/docs/intellij/deployment.html#specifying-a-release-channel
- channels = properties("pluginVersion").map { listOf(it.split('-').getOrElse(1) { "default" }.split('.').first()) }
- }
+ wrapper {
+ gradleVersion = properties("gradleVersion").get()
+ }
+
+ patchPluginXml {
+ version = properties("pluginVersion")
+ sinceBuild = properties("pluginSinceBuild")
+ untilBuild = properties("pluginUntilBuild")
+ }
+
+ downloadRobotServerPlugin {
+ version.set(remoteRobotVersion)
+ }
+
+ // Configure UI tests plugin
+ // Read more: https://github.com/JetBrains/intellij-ui-test-robot
+ runIdeForUiTests {
+ systemProperty("robot-server.port", "8082")
+ systemProperty("ide.mac.message.dialogs.as.sheets", "false")
+ systemProperty("jb.privacy.policy.text", "")
+ systemProperty("jb.consents.confirmation.enabled", "false")
+ systemProperty("ide.mac.file.chooser.native", "false")
+ systemProperty("jbScreenMenuBar.enabled", "false")
+ systemProperty("apple.laf.useScreenMenuBar", "false")
+ systemProperty("idea.trust.all.projects", "true")
+ systemProperty("ide.show.tips.on.startup.default.value", "false")
+ systemProperty("eap.require.license", "false")
+
+ }
+
+ test {
+ useJUnitPlatform()
+ }
+
+ signPlugin {
+ certificateChain = environment("CERTIFICATE_CHAIN")
+ privateKey = environment("PRIVATE_KEY")
+ password = environment("PRIVATE_KEY_PASSWORD")
+ }
+
+ publishPlugin {
+ token = environment("PUBLISH_TOKEN")
+ // The pluginVersion is based on the SemVer (https://semver.org) and supports pre-release labels, like 2.1.7-alpha.3
+ // Specify pre-release label to publish the plugin in a custom Release Channel automatically. Read more:
+ // https://plugins.jetbrains.com/docs/intellij/deployment.html#specifying-a-release-channel
+ channels = properties("pluginVersion").map { listOf(it.split('-').getOrElse(1) { "default" }.split('.').first()) }
+ }
}
diff --git a/cliff.toml b/cliff.toml
index 40ada13..5761168 100644
--- a/cliff.toml
+++ b/cliff.toml
@@ -34,15 +34,15 @@ filter_unconventional = true
filter_commits = true
commit_preprocessors = [
- { pattern = "\\(#([0-9]+)\\)", replace = "([#${1}](https://github.com/biomejs/biome-intellij/pull/${1}))"}
+ { pattern = "\\(#([0-9]+)\\)", replace = "([#${1}](https://github.com/biomejs/biome-intellij/pull/${1}))" }
]
# Commit parsers to use for parsing commits
commit_parsers = [
- { message = "^feat", group = "Features" },
- { message = "^fix", group = "Bug Fixes" },
- { message = "^doc", group = "Documentation"},
+ { message = "^feat", group = "Features" },
+ { message = "^fix", group = "Bug Fixes" },
+ { message = "^doc", group = "Documentation" },
]
# Regex for matching git tags
-tag_pattern = "v[0-9].*"
\ No newline at end of file
+tag_pattern = "v[0-9].*"
diff --git a/gradle.properties b/gradle.properties
index a7511df..b5ef2d8 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,33 +1,24 @@
# IntelliJ Platform Artifacts Repositories -> https://plugins.jetbrains.com/docs/intellij/intellij-artifacts.html
-
-pluginGroup = com.github.biomejs.intellijbiome
-pluginName = Biome
-pluginRepositoryUrl = https://github.com/biomejs/biome
+pluginGroup=com.github.biomejs.intellijbiome
+pluginName=Biome
+pluginRepositoryUrl=https://github.com/biomejs/biome
# SemVer format -> https://semver.org
-pluginVersion = 0.0.7
-
+pluginVersion=0.0.7
# Supported build number ranges and IntelliJ Platform versions -> https://plugins.jetbrains.com/docs/intellij/build-number-ranges.html
-pluginSinceBuild = 232
-
+pluginSinceBuild=233
# IntelliJ Platform Properties -> https://plugins.jetbrains.com/docs/intellij/tools-gradle-intellij-plugin.html#configuration-intellij-extension
-platformType = IU
-platformVersion = 233-EAP-SNAPSHOT
-
+platformType=IU
+platformVersion=233-EAP-SNAPSHOT
# Plugin Dependencies -> https://plugins.jetbrains.com/docs/intellij/plugin-dependencies.html
# Example: platformPlugins = com.intellij.java, com.jetbrains.php:203.4449.22
platformPlugins=JavaScript
-
# Gradle Releases -> https://github.com/gradle/gradle/releases
-gradleVersion = 8.2.1
-
+gradleVersion=8.5
# Opt-out flag for bundling Kotlin standard library -> https://jb.gg/intellij-platform-kotlin-stdlib
-kotlin.stdlib.default.dependency = false
-
+kotlin.stdlib.default.dependency=false
# Enable Gradle Configuration Cache -> https://docs.gradle.org/current/userguide/configuration_cache.html
-org.gradle.configuration-cache = true
-
+org.gradle.configuration-cache=true
# Enable Gradle Build Cache -> https://docs.gradle.org/current/userguide/build_cache.html
-org.gradle.caching = true
-
+org.gradle.caching=true
# Enable Gradle Kotlin DSL Lazy Property Assignment -> https://docs.gradle.org/current/userguide/kotlin_dsl.html#kotdsl:assignment
-systemProp.org.gradle.unsafe.kotlin.assignment = true
+systemProp.org.gradle.unsafe.kotlin.assignment=true
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 99297ef..06c073a 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -1,10 +1,10 @@
[versions]
# libraries
-annotations = "24.0.1"
+annotations = "24.1.0"
# plugins
-kotlin = "1.9.0"
-gradleIntelliJPlugin = "1.15.0"
+kotlin = "1.9.21"
+gradleIntelliJPlugin = "1.16.1"
[libraries]
annotations = { group = "org.jetbrains", name = "annotations", version.ref = "annotations" }
diff --git a/src/main/kotlin/com/github/biomejs/intellijbiome/BiomePackage.kt b/src/main/kotlin/com/github/biomejs/intellijbiome/BiomePackage.kt
new file mode 100644
index 0000000..2b69de7
--- /dev/null
+++ b/src/main/kotlin/com/github/biomejs/intellijbiome/BiomePackage.kt
@@ -0,0 +1,52 @@
+package com.github.biomejs.intellijbiome
+
+import com.github.biomejs.intellijbiome.extensions.runWithNodeInterpreter
+import com.github.biomejs.intellijbiome.settings.BiomeSettings
+import com.intellij.execution.configurations.GeneralCommandLine
+import com.intellij.execution.util.ExecUtil
+import com.intellij.javascript.nodejs.library.node_modules.NodeModulesDirectoryManager
+import com.intellij.openapi.project.Project
+
+object BiomePackage {
+ fun versionNumber(project: Project, binaryPath: String?): String? {
+ if (binaryPath.isNullOrEmpty()) {
+ return null
+ }
+
+ val versionRegex = Regex("\\d{1,2}\\.\\d{1,2}\\.\\d{1,3}")
+ val commandLine = GeneralCommandLine().runWithNodeInterpreter(project, binaryPath).apply {
+ addParameter("--version")
+ }
+
+ val output = ExecUtil.execAndGetOutput(commandLine)
+ val matchResult = versionRegex.find(output.stdout)
+ return matchResult?.value
+ }
+
+ fun binaryPath(project: Project): String? {
+ val directoryManager = NodeModulesDirectoryManager.getInstance(project)
+ val executablePath = BiomeSettings.getInstance(project).executablePath
+
+ if (executablePath.isNotEmpty()) {
+ return executablePath
+ }
+
+ val binaryFile = directoryManager.nodeModulesDirs
+ .asSequence()
+ .mapNotNull { it.findFileByRelativePath("@biomejs/biome/bin/biome") }
+ .filter { it.isValid }
+ .firstOrNull()
+
+ return binaryFile?.path
+ }
+
+ fun configPath(project: Project): String? {
+ val configPath = BiomeSettings.getInstance(project).configPath
+
+ if (configPath.isNotEmpty()) {
+ return configPath
+ }
+
+ return null
+ }
+}
diff --git a/src/main/kotlin/com/github/biomejs/intellijbiome/BiomeRunner.kt b/src/main/kotlin/com/github/biomejs/intellijbiome/BiomeRunner.kt
new file mode 100644
index 0000000..0a63795
--- /dev/null
+++ b/src/main/kotlin/com/github/biomejs/intellijbiome/BiomeRunner.kt
@@ -0,0 +1,41 @@
+package com.github.biomejs.intellijbiome
+
+import com.intellij.execution.configurations.GeneralCommandLine
+import com.intellij.openapi.editor.Document
+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
+
+interface BiomeRunner {
+ companion object {
+ val DEFAULT_TIMEOUT = 30000.milliseconds
+ }
+
+ fun format(request: Request): Response
+ fun applySafeFixes(request: Request): Response
+ fun applyUnsafeFixes(request: Request): Response
+ fun createCommandLine(file: VirtualFile, action: String, args: String? = null): GeneralCommandLine
+
+
+ data class Request(
+ val document: Document,
+ val virtualFile: VirtualFile,
+ val timeout: Duration,
+ 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
new file mode 100644
index 0000000..bcf7098
--- /dev/null
+++ b/src/main/kotlin/com/github/biomejs/intellijbiome/BiomeStdinRunner.kt
@@ -0,0 +1,122 @@
+package com.github.biomejs.intellijbiome
+
+import com.github.biomejs.intellijbiome.extensions.isSuccess
+import com.github.biomejs.intellijbiome.extensions.runProcessFuture
+import com.github.biomejs.intellijbiome.extensions.runWithNodeInterpreter
+import com.intellij.execution.ExecutionException
+import com.intellij.execution.configurations.GeneralCommandLine
+import com.intellij.openapi.progress.util.ProgressIndicatorUtils
+import com.intellij.openapi.project.Project
+import com.intellij.openapi.vfs.VirtualFile
+import com.intellij.util.SmartList
+import java.io.File
+import java.util.concurrent.CompletableFuture
+import java.util.concurrent.TimeUnit
+import kotlin.time.Duration
+
+class BiomeStdinRunner(private val project: Project) : BiomeRunner {
+ override fun format(request: BiomeRunner.Request): BiomeRunner.Response {
+ val commandLine = createCommandLine(request.virtualFile, "format")
+ val file = request.virtualFile
+ val timeout = request.timeout
+ val future = startTheFuture(
+ BiomeBundle.message(
+ "biome.failed.to.format.file",
+ file.name
+ ), timeout
+ )
+
+ commandLine.runProcessFuture().thenAccept { result ->
+ if (result.processEvent.isSuccess) {
+ future.complete(BiomeRunner.Response.Success(result.processOutput.stdout))
+ } else {
+ future.complete(
+ BiomeRunner.Response.Failure(
+ BiomeBundle.message("biome.failed.to.format.file", file.name),
+ result.processOutput.stderr, result.processEvent.exitCode
+ )
+ )
+ }
+ }
+
+ return ProgressIndicatorUtils.awaitWithCheckCanceled(future)
+ }
+
+ override fun applySafeFixes(request: BiomeRunner.Request): BiomeRunner.Response {
+ val commandLine = createCommandLine(request.virtualFile, "lint", "--apply")
+ return runFixCommand(request, commandLine)
+ }
+
+ override fun applyUnsafeFixes(request: BiomeRunner.Request): BiomeRunner.Response {
+ val commandLine = createCommandLine(request.virtualFile, "lint", "--apply-unsafe")
+ return runFixCommand(request, commandLine)
+ }
+
+ private fun runFixCommand(
+ request: BiomeRunner.Request,
+ commandLine: GeneralCommandLine
+ ): BiomeRunner.Response {
+ val file = request.virtualFile
+ val timeout = request.timeout
+ val future = startTheFuture(
+ BiomeBundle.message(
+ "biome.failed.to.fix.file",
+ file.name
+ ), timeout
+ )
+
+ commandLine.runProcessFuture().thenAccept { result ->
+ if (result.processEvent.isSuccess) {
+ future.complete(BiomeRunner.Response.Success(result.processOutput.stdout))
+ } else {
+ future.complete(
+ BiomeRunner.Response.Failure(
+ BiomeBundle.message("biome.failed.to.fix.file", file.name),
+ result.processOutput.stderr, result.processEvent.exitCode
+ )
+ )
+ }
+ }
+
+ return ProgressIndicatorUtils.awaitWithCheckCanceled(future)
+ }
+
+ override fun createCommandLine(file: VirtualFile, action: String, args: String?): GeneralCommandLine {
+ val configPath = BiomePackage.configPath(project)
+ val exePath = BiomePackage.binaryPath(project)
+ val params = SmartList(action, "--stdin-file-path", file.path)
+
+ if (!args.isNullOrEmpty()) {
+ params.add(args)
+ }
+
+ if (!configPath.isNullOrEmpty()) {
+ params.add("--config-path")
+ params.add(configPath)
+ }
+
+ if (exePath.isNullOrEmpty()) {
+ throw ExecutionException(BiomeBundle.message("biome.language.server.not.found"))
+ }
+
+ return GeneralCommandLine().runWithNodeInterpreter(project, exePath).apply {
+ withInput(File(file.path))
+ addParameters(params)
+ }
+ }
+
+ private fun startTheFuture(
+ timeoutMessage: String,
+ timeout: Duration
+ ): CompletableFuture {
+ val future = CompletableFuture()
+ .completeOnTimeout(
+ BiomeRunner.Response.Failure(
+ timeoutMessage, "Timeout exceeded", null
+ ),
+ timeout.inWholeMilliseconds, TimeUnit.MILLISECONDS
+ )
+ return future
+ }
+
+}
diff --git a/src/main/kotlin/com/github/biomejs/intellijbiome/BiomeUtils.kt b/src/main/kotlin/com/github/biomejs/intellijbiome/BiomeUtils.kt
deleted file mode 100644
index d8a154d..0000000
--- a/src/main/kotlin/com/github/biomejs/intellijbiome/BiomeUtils.kt
+++ /dev/null
@@ -1,83 +0,0 @@
-package com.github.biomejs.intellijbiome
-
-import com.github.biomejs.intellijbiome.settings.BiomeSettings
-import com.intellij.execution.configurations.GeneralCommandLine
-import com.intellij.execution.util.ExecUtil
-import com.intellij.javascript.nodejs.library.node_modules.NodeModulesDirectoryManager
-import com.intellij.openapi.diagnostic.Logger
-import com.intellij.openapi.project.Project
-import com.intellij.openapi.vfs.VirtualFile
-import com.intellij.util.SmartList
-import java.io.File
-import com.intellij.javascript.nodejs.interpreter.NodeCommandLineConfigurator
-import com.intellij.javascript.nodejs.interpreter.NodeJsInterpreterManager
-import com.intellij.javascript.nodejs.interpreter.local.NodeJsLocalInterpreter
-import com.intellij.javascript.nodejs.interpreter.wsl.WslNodeInterpreter
-import com.intellij.execution.ExecutionException
-
-object BiomeUtils {
- fun isSupportedFileType(file: VirtualFile): Boolean = when (file.extension) {
- "js", "mjs", "cjs", "jsx", "ts", "mts", "cts", "tsx", "d.ts", "json", "jsonc" -> true
- else -> false
- }
-
- fun getBiomeVersion(project: Project, binaryPath: String): String? {
- if (binaryPath.isEmpty()) {
- return null
- }
-
- val versionRegex = Regex("\\d{1,2}\\.\\d{1,2}\\.\\d{1,3}")
-
- val commandLine = createNodeCommandLine(project, binaryPath).apply {
- addParameter("--version")
- }
-
- val output = ExecUtil.execAndGetOutput(commandLine)
-
- val matchResult = versionRegex.find(output.stdout)
-
- return matchResult?.value
- }
-
- fun getBiomeExecutablePath(project: Project): String? {
- val directoryManager = NodeModulesDirectoryManager.getInstance(project)
- val executablePath = BiomeSettings.getInstance(project).executablePath
-
- if (!executablePath.isEmpty()) {
- return executablePath
- }
-
- val biomeBinFile = directoryManager.nodeModulesDirs
- .asSequence()
- .mapNotNull { it.findFileByRelativePath("@biomejs/biome/bin/biome") }
- .filter { it.isValid }
- .firstOrNull()
-
- return biomeBinFile?.path
- }
-
- fun getBiomeConfigPath(project: Project): String? {
- val configPath = BiomeSettings.getInstance(project).configPath
-
- if (!configPath.isEmpty()) {
- return configPath
- }
-
- return null
- }
-
- fun createNodeCommandLine(project: Project, executable: String): GeneralCommandLine {
- val interpreter = NodeJsInterpreterManager.getInstance(project).interpreter
- if (interpreter !is NodeJsLocalInterpreter && interpreter !is WslNodeInterpreter) {
- throw ExecutionException(BiomeBundle.message("biome.interpreter.not.configured"))
- }
-
- return GeneralCommandLine().apply {
- withParentEnvironmentType(GeneralCommandLine.ParentEnvironmentType.CONSOLE)
- addParameter(executable)
-
- NodeCommandLineConfigurator.find(interpreter)
- .configure(this, NodeCommandLineConfigurator.defaultOptions(project))
- }
- }
-}
diff --git a/src/main/kotlin/com/github/biomejs/intellijbiome/actions/ApplySafeFixesOnSaveAction.kt b/src/main/kotlin/com/github/biomejs/intellijbiome/actions/ApplySafeFixesOnSaveAction.kt
new file mode 100644
index 0000000..0c283d3
--- /dev/null
+++ b/src/main/kotlin/com/github/biomejs/intellijbiome/actions/ApplySafeFixesOnSaveAction.kt
@@ -0,0 +1,24 @@
+package com.github.biomejs.intellijbiome.actions
+
+import com.github.biomejs.intellijbiome.BiomeBundle
+import com.github.biomejs.intellijbiome.BiomeStdinRunner
+import com.github.biomejs.intellijbiome.settings.BiomeSettings
+import com.intellij.ide.actionsOnSave.impl.ActionsOnSaveFileDocumentManagerListener
+import com.intellij.openapi.editor.Document
+import com.intellij.openapi.project.Project
+
+
+class ApplySafeFixesOnSaveAction : ActionsOnSaveFileDocumentManagerListener.ActionOnSave() {
+ override fun isEnabledForProject(project: Project): Boolean =
+ BiomeSettings.getInstance(project).applySafeFixesOnSave
+
+ override fun processDocuments(project: Project, documents: Array) {
+ val runner = BiomeStdinRunner(project)
+
+ OnSaveHelper().formatDocuments(
+ project,
+ documents.filterNotNull().toList(),
+ BiomeBundle.message("biome.apply.safe.fix.with.biome")
+ ) { request -> runner.applySafeFixes(request) }
+ }
+}
diff --git a/src/main/kotlin/com/github/biomejs/intellijbiome/actions/ApplyUnsafeFixesOnSaveAction.kt b/src/main/kotlin/com/github/biomejs/intellijbiome/actions/ApplyUnsafeFixesOnSaveAction.kt
new file mode 100644
index 0000000..1e09bb7
--- /dev/null
+++ b/src/main/kotlin/com/github/biomejs/intellijbiome/actions/ApplyUnsafeFixesOnSaveAction.kt
@@ -0,0 +1,25 @@
+package com.github.biomejs.intellijbiome.actions
+
+import com.github.biomejs.intellijbiome.BiomeBundle
+import com.github.biomejs.intellijbiome.BiomeStdinRunner
+import com.github.biomejs.intellijbiome.settings.BiomeSettings
+import com.intellij.ide.actionsOnSave.impl.ActionsOnSaveFileDocumentManagerListener
+import com.intellij.openapi.editor.Document
+import com.intellij.openapi.project.Project
+
+
+class ApplyUnsafeFixesOnSaveAction : ActionsOnSaveFileDocumentManagerListener.ActionOnSave() {
+ override fun isEnabledForProject(project: Project): Boolean =
+ BiomeSettings.getInstance(project).applyUnsafeFixesOnSave
+
+ override fun processDocuments(project: Project, documents: Array) {
+ val runner = BiomeStdinRunner(project)
+
+ OnSaveHelper().formatDocuments(
+ project,
+ documents.filterNotNull().toList(),
+ BiomeBundle.message("biome.apply.unsafe.fix.with.biome")
+ ) { request -> runner.applyUnsafeFixes(request) }
+ }
+
+}
diff --git a/src/main/kotlin/com/github/biomejs/intellijbiome/actions/FormatOnSaveAction.kt b/src/main/kotlin/com/github/biomejs/intellijbiome/actions/FormatOnSaveAction.kt
new file mode 100644
index 0000000..7e4e925
--- /dev/null
+++ b/src/main/kotlin/com/github/biomejs/intellijbiome/actions/FormatOnSaveAction.kt
@@ -0,0 +1,23 @@
+package com.github.biomejs.intellijbiome.actions
+
+import com.github.biomejs.intellijbiome.BiomeBundle
+import com.github.biomejs.intellijbiome.BiomeStdinRunner
+import com.github.biomejs.intellijbiome.settings.BiomeSettings
+import com.intellij.ide.actionsOnSave.impl.ActionsOnSaveFileDocumentManagerListener
+import com.intellij.openapi.editor.Document
+import com.intellij.openapi.project.Project
+
+
+class FormatOnSaveAction : ActionsOnSaveFileDocumentManagerListener.ActionOnSave() {
+ override fun isEnabledForProject(project: Project): Boolean = BiomeSettings.getInstance(project).formatOnSave
+
+ override fun processDocuments(project: Project, documents: Array) {
+ val runner = BiomeStdinRunner(project)
+
+ OnSaveHelper().formatDocuments(
+ project,
+ documents.filterNotNull().toList(),
+ BiomeBundle.message("biome.apply.safe.fix.with.biome")
+ ) { request -> runner.format(request) }
+ }
+}
diff --git a/src/main/kotlin/com/github/biomejs/intellijbiome/actions/OnSaveHelper.kt b/src/main/kotlin/com/github/biomejs/intellijbiome/actions/OnSaveHelper.kt
new file mode 100644
index 0000000..060a9ea
--- /dev/null
+++ b/src/main/kotlin/com/github/biomejs/intellijbiome/actions/OnSaveHelper.kt
@@ -0,0 +1,79 @@
+package com.github.biomejs.intellijbiome.actions
+
+import com.github.biomejs.intellijbiome.BiomeRunner
+import com.github.biomejs.intellijbiome.settings.BiomeSettings
+import com.intellij.lang.javascript.linter.GlobPatternUtil
+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.progress.ProcessCanceledException
+import com.intellij.openapi.progress.ProgressIndicator
+import com.intellij.openapi.progress.ProgressManager
+import com.intellij.openapi.progress.Task
+import com.intellij.openapi.project.Project
+
+class OnSaveHelper {
+ companion object {
+ val LOG = thisLogger()
+ }
+
+ fun formatDocuments(
+ project: Project,
+ documents: List,
+ commandDescription: String,
+ callback: (request: BiomeRunner.Request) -> BiomeRunner.Response
+ ) {
+ val manager = FileDocumentManager.getInstance()
+ val settings = BiomeSettings.getInstance(project)
+ val requests = documents
+ .mapNotNull { document -> manager.getFile(document)?.let { document to it } }
+ .filter { GlobPatternUtil.isFileMatchingGlobPattern(project, settings.formatFilePattern, it.second) }
+ .map { BiomeRunner.Request(it.first, it.second, BiomeRunner.DEFAULT_TIMEOUT, commandDescription) }
+
+ runCatching {
+ ProgressManager.getInstance().run(
+ object : Task.Backgroundable(project, commandDescription, true) {
+ override fun run(indicator: ProgressIndicator) {
+ indicator.text = commandDescription
+
+ requests.forEach { request ->
+ val response = callback(request)
+
+ if (!indicator.isCanceled) {
+ applyChanges(project, request, response)
+ }
+ }
+ }
+ }
+ )
+ }.onFailure { exception ->
+ when (exception) {
+ is ProcessCanceledException -> {}
+
+ else -> {
+ LOG.error(exception)
+ }
+ }
+ }
+ }
+
+ private fun applyChanges(
+ project: Project,
+ request: BiomeRunner.Request,
+ response: BiomeRunner.Response
+ ) {
+ when (response) {
+ is BiomeRunner.Response.Success -> {
+ WriteCommandAction.writeCommandAction(project)
+ .withName(request.commandDescription)
+ .run { request.document.setText(response.code) }
+ }
+
+ is BiomeRunner.Response.Failure -> {
+ LOG.error(response.title)
+ }
+ }
+ }
+
+}
diff --git a/src/main/kotlin/com/github/biomejs/intellijbiome/actions/ReformatWithBiomeAction.kt b/src/main/kotlin/com/github/biomejs/intellijbiome/actions/ReformatWithBiomeAction.kt
new file mode 100644
index 0000000..2324362
--- /dev/null
+++ b/src/main/kotlin/com/github/biomejs/intellijbiome/actions/ReformatWithBiomeAction.kt
@@ -0,0 +1,38 @@
+package com.github.biomejs.intellijbiome.actions
+
+import com.github.biomejs.intellijbiome.settings.BiomeSettings
+import com.intellij.openapi.actionSystem.ActionUpdateThread
+import com.intellij.openapi.actionSystem.AnAction
+import com.intellij.openapi.actionSystem.AnActionEvent
+import com.intellij.openapi.actionSystem.CommonDataKeys
+import com.intellij.openapi.editor.Editor
+import com.intellij.openapi.project.DumbAware
+
+
+class ReformatWithBiomeAction : AnAction(), DumbAware {
+ override fun actionPerformed(actionEvent: AnActionEvent) {
+ val project = actionEvent.project
+ if (project == null || project.isDefault) return
+
+ val editor: Editor? = actionEvent.getData(CommonDataKeys.EDITOR)
+
+ if (editor != null) {
+ FormatOnSaveAction().processDocuments(project, arrayOf(editor.document))
+ }
+ }
+
+ override fun update(actionEvent: AnActionEvent) {
+ val project = actionEvent.project
+ if (project == null || project.isDefault) {
+ actionEvent.presentation.isEnabledAndVisible = false
+ return
+ }
+
+ val settings = BiomeSettings.getInstance(project)
+ actionEvent.presentation.isEnabledAndVisible = settings.isEnabled()
+ }
+
+ override fun getActionUpdateThread(): ActionUpdateThread {
+ return ActionUpdateThread.BGT
+ }
+}
diff --git a/src/main/kotlin/com/github/biomejs/intellijbiome/actions/RestartBiomeServerAction.kt b/src/main/kotlin/com/github/biomejs/intellijbiome/actions/RestartBiomeServerAction.kt
index aeab4f5..3fe7ceb 100644
--- a/src/main/kotlin/com/github/biomejs/intellijbiome/actions/RestartBiomeServerAction.kt
+++ b/src/main/kotlin/com/github/biomejs/intellijbiome/actions/RestartBiomeServerAction.kt
@@ -1,9 +1,9 @@
package com.github.biomejs.intellijbiome.actions
+import com.github.biomejs.intellijbiome.services.BiomeServerService
import com.intellij.openapi.actionSystem.AnAction
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.components.service
-import com.github.biomejs.intellijbiome.services.BiomeServerService
class RestartBiomeServerAction : AnAction() {
override fun actionPerformed(actionEvent: AnActionEvent) {
diff --git a/src/main/kotlin/com/github/biomejs/intellijbiome/extensions/CommandLineExt.kt b/src/main/kotlin/com/github/biomejs/intellijbiome/extensions/CommandLineExt.kt
new file mode 100644
index 0000000..98a4a43
--- /dev/null
+++ b/src/main/kotlin/com/github/biomejs/intellijbiome/extensions/CommandLineExt.kt
@@ -0,0 +1,52 @@
+package com.github.biomejs.intellijbiome.extensions
+
+import com.github.biomejs.intellijbiome.BiomeBundle
+import com.intellij.execution.ExecutionException
+import com.intellij.execution.configurations.GeneralCommandLine
+import com.intellij.execution.process.CapturingProcessAdapter
+import com.intellij.execution.process.CapturingProcessHandler
+import com.intellij.execution.process.ProcessEvent
+import com.intellij.execution.process.ProcessOutput
+import com.intellij.javascript.nodejs.interpreter.NodeCommandLineConfigurator
+import com.intellij.javascript.nodejs.interpreter.NodeJsInterpreterManager
+import com.intellij.javascript.nodejs.interpreter.local.NodeJsLocalInterpreter
+import com.intellij.javascript.nodejs.interpreter.wsl.WslNodeInterpreter
+import com.intellij.openapi.project.Project
+import java.nio.charset.StandardCharsets
+import java.util.concurrent.CompletableFuture
+
+class ProcessResult(val processEvent: ProcessEvent, val processOutput: ProcessOutput)
+
+val ProcessEvent.isSuccess: Boolean get() = exitCode == 0
+
+fun GeneralCommandLine.runProcessFuture(): CompletableFuture {
+ val future = CompletableFuture()
+
+ val processHandler = CapturingProcessHandler(this.withCharset(StandardCharsets.UTF_8))
+
+ processHandler.addProcessListener(object : CapturingProcessAdapter() {
+ override fun processTerminated(event: ProcessEvent) {
+ future.complete(ProcessResult(event, output))
+ }
+ })
+
+ processHandler.startNotify()
+
+ return future
+}
+
+fun GeneralCommandLine.runWithNodeInterpreter(project: Project, command: String): GeneralCommandLine {
+ val interpreter = NodeJsInterpreterManager.getInstance(project).interpreter
+ if (interpreter !is NodeJsLocalInterpreter && interpreter !is WslNodeInterpreter) {
+ throw ExecutionException(BiomeBundle.message("biome.interpreter.not.configured"))
+ }
+
+ return this.apply {
+ withParentEnvironmentType(GeneralCommandLine.ParentEnvironmentType.CONSOLE)
+ addParameter(command)
+ withWorkDirectory(project.basePath)
+
+ NodeCommandLineConfigurator.find(interpreter)
+ .configure(this, NodeCommandLineConfigurator.defaultOptions(project))
+ }
+}
diff --git a/src/main/kotlin/com/github/biomejs/intellijbiome/formatter/BiomeFormatterProvider.kt b/src/main/kotlin/com/github/biomejs/intellijbiome/formatter/FormatterProvider.kt
similarity index 58%
rename from src/main/kotlin/com/github/biomejs/intellijbiome/formatter/BiomeFormatterProvider.kt
rename to src/main/kotlin/com/github/biomejs/intellijbiome/formatter/FormatterProvider.kt
index 9089d6c..1756383 100644
--- a/src/main/kotlin/com/github/biomejs/intellijbiome/formatter/BiomeFormatterProvider.kt
+++ b/src/main/kotlin/com/github/biomejs/intellijbiome/formatter/FormatterProvider.kt
@@ -1,58 +1,42 @@
package com.github.biomejs.intellijbiome.formatter
import com.github.biomejs.intellijbiome.BiomeBundle
-import com.github.biomejs.intellijbiome.BiomeUtils
-import com.intellij.execution.configurations.GeneralCommandLine
+import com.github.biomejs.intellijbiome.BiomeStdinRunner
+import com.github.biomejs.intellijbiome.settings.BiomeSettings
+import com.intellij.execution.ExecutionException
import com.intellij.execution.process.CapturingProcessAdapter
import com.intellij.execution.process.OSProcessHandler
import com.intellij.execution.process.ProcessEvent
import com.intellij.formatting.service.AsyncDocumentFormattingService
import com.intellij.formatting.service.AsyncFormattingRequest
import com.intellij.formatting.service.FormattingService.Feature
-import com.intellij.openapi.diagnostic.thisLogger
+import com.intellij.openapi.application.ApplicationInfo
import com.intellij.psi.PsiFile
-import com.intellij.util.SmartList
import org.jetbrains.annotations.NotNull
import java.nio.charset.StandardCharsets
-import java.util.EnumSet
-import com.intellij.execution.ExecutionException
-import com.intellij.execution.process.CapturingProcessHandler
-import com.intellij.openapi.progress.util.ProgressIndicatorBase
-
-class BiomeFormatterProvider : AsyncDocumentFormattingService() {
- override fun getFeatures(): MutableSet = EnumSet.noneOf(Feature::class.java)
-
- override fun canFormat(file: PsiFile): Boolean =
- file.virtualFile?.let { BiomeUtils.isSupportedFileType(it) } ?: false
+import java.util.*
- override fun getNotificationGroupId(): String = "Biome"
-
- override fun getName(): String = "Biome"
+class FormatterProvider : AsyncDocumentFormattingService() {
+ override fun getFeatures(): MutableSet = FEATURES
+ override fun getNotificationGroupId(): String = NOTIFICATION_GROUP_ID
+ override fun getName(): String = NAME
+ override fun canFormat(file: PsiFile): Boolean {
+ // IDEs with version >= 2023.3 uses native LSP formatter
+ return ApplicationInfo.getInstance().build.baselineVersion < 233
+ }
override fun createFormattingTask(request: AsyncFormattingRequest): FormattingTask? {
- val ioFile = request.ioFile ?: return null
+ val file = request.context.virtualFile ?: return null
val project = request.context.project
- val configPath = BiomeUtils.getBiomeConfigPath(project)
-
- val params = SmartList("format", "--stdin-file-path", ioFile.path)
+ val formatterRunner = BiomeStdinRunner(project)
+ val settings = BiomeSettings.getInstance(project)
- if (!configPath.isNullOrEmpty()) {
- params.add("--config-path")
- params.add(configPath)
- }
-
- val exePath = BiomeUtils.getBiomeExecutablePath(project)
-
- if (exePath.isNullOrEmpty()) {
- throw ExecutionException(BiomeBundle.message("biome.language.server.not.found"))
+ if (!settings.canFormat(project, file)) {
+ return null
}
try {
- val commandLine: GeneralCommandLine = BiomeUtils.createNodeCommandLine(project, exePath).apply {
- withInput(ioFile)
- addParameters(params)
- withWorkDirectory(project.basePath)
- }
+ val commandLine = formatterRunner.createCommandLine(file, "format")
val handler = OSProcessHandler(commandLine.withCharset(StandardCharsets.UTF_8))
return object : FormattingTask {
@@ -85,4 +69,10 @@ class BiomeFormatterProvider : AsyncDocumentFormattingService() {
return null
}
}
+
+ companion object {
+ val NAME: String = BiomeBundle.message("biome.formatting.service.name")
+ const val NOTIFICATION_GROUP_ID = "Biome"
+ val FEATURES: EnumSet = EnumSet.noneOf(Feature::class.java)
+ }
}
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 c3d7980..c962cd2 100644
--- a/src/main/kotlin/com/github/biomejs/intellijbiome/listeners/BiomeConfigListener.kt
+++ b/src/main/kotlin/com/github/biomejs/intellijbiome/listeners/BiomeConfigListener.kt
@@ -1,10 +1,10 @@
package com.github.biomejs.intellijbiome.listeners
+import com.github.biomejs.intellijbiome.services.BiomeServerService
+import com.intellij.openapi.components.service
import com.intellij.openapi.project.Project
import com.intellij.openapi.vfs.newvfs.BulkFileListener
import com.intellij.openapi.vfs.newvfs.events.VFileEvent
-import com.intellij.openapi.components.service
-import com.github.biomejs.intellijbiome.services.BiomeServerService
class BiomeConfigListener(val project: Project) : BulkFileListener {
override fun after(events: MutableList) {
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 345439e..2788a3f 100644
--- a/src/main/kotlin/com/github/biomejs/intellijbiome/lsp/BiomeLspServerSupportProvider.kt
+++ b/src/main/kotlin/com/github/biomejs/intellijbiome/lsp/BiomeLspServerSupportProvider.kt
@@ -1,63 +1,76 @@
package com.github.biomejs.intellijbiome.lsp
import com.github.biomejs.intellijbiome.BiomeBundle
-import com.github.biomejs.intellijbiome.BiomeUtils
+import com.github.biomejs.intellijbiome.BiomePackage
+import com.github.biomejs.intellijbiome.extensions.runWithNodeInterpreter
import com.github.biomejs.intellijbiome.listeners.BIOME_CONFIG_RESOLVED_TOPIC
+import com.github.biomejs.intellijbiome.settings.BiomeSettings
import com.intellij.execution.ExecutionException
import com.intellij.execution.configurations.GeneralCommandLine
-import com.intellij.openapi.application.ApplicationManager
-import com.intellij.openapi.diagnostic.thisLogger
import com.intellij.openapi.project.Project
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.platform.lsp.api.LspServerSupportProvider
import com.intellij.platform.lsp.api.ProjectWideLspServerDescriptor
-import com.intellij.platform.lsp.api.customization.LspCodeActionsSupport
-import com.intellij.platform.lsp.api.customization.LspDiagnosticsSupport
+import com.intellij.platform.lsp.api.customization.LspFormattingSupport
import com.intellij.util.SmartList
-import org.eclipse.lsp4j.*
@Suppress("UnstableApiUsage")
class BiomeLspServerSupportProvider : LspServerSupportProvider {
-
override fun fileOpened(
project: Project,
file: VirtualFile,
serverStarter: LspServerSupportProvider.LspServerStarter
) {
- if (BiomeUtils.isSupportedFileType(file)) {
- val executable = BiomeUtils.getBiomeExecutablePath(project) ?: return
- serverStarter.ensureServerStarted(BiomeLspServerDescriptor(project, executable))
- }
+ val executable = BiomePackage.binaryPath(project) ?: return
+ serverStarter.ensureServerStarted(LspServerDescriptor(project, executable))
}
}
@Suppress("UnstableApiUsage")
-private class BiomeLspServerDescriptor(project: Project, val executable: String) :
+private class LspServerDescriptor(project: Project, val executable: String) :
ProjectWideLspServerDescriptor(project, "Biome") {
- override fun isSupportedFile(file: VirtualFile) = BiomeUtils.isSupportedFileType(file)
+
+ override fun isSupportedFile(file: VirtualFile): Boolean {
+ val settings = BiomeSettings.getInstance(project)
+ if (!settings.isEnabled()) {
+ return false
+ }
+
+ return BiomeSettings.getInstance(project).canLint(project, file)
+ }
+
override fun createCommandLine(): GeneralCommandLine {
- val configPath = BiomeUtils.getBiomeConfigPath(project)
- val params = SmartList("lsp-proxy")
+ val configPath = BiomePackage.configPath(project)
+ val params = SmartList("lsp-proxy")
- if (!configPath.isNullOrEmpty()) {
- params.add("--config-path")
- params.add(configPath)
- }
+ if (!configPath.isNullOrEmpty()) {
+ params.add("--config-path")
+ params.add(configPath)
+ }
if (executable.isEmpty()) {
throw ExecutionException(BiomeBundle.message("biome.language.server.not.found"))
}
- val version = BiomeUtils.getBiomeVersion(project, executable)
+ val version = BiomePackage.versionNumber(project, executable)
version?.let { project.messageBus.syncPublisher(BIOME_CONFIG_RESOLVED_TOPIC).resolved(it) }
- return BiomeUtils.createNodeCommandLine(project, executable).apply {
+ return GeneralCommandLine().runWithNodeInterpreter(project, executable).apply {
addParameters(params)
}
-
}
override val lspGoToDefinitionSupport = false
override val lspCompletionSupport = null
+
+ override val lspFormattingSupport = object : LspFormattingSupport() {
+ override fun shouldFormatThisFileExclusivelyByServer(
+ file: VirtualFile,
+ ideCanFormatThisFileItself: Boolean,
+ serverExplicitlyWantsToFormatThisFile: Boolean
+ ): Boolean {
+ return BiomeSettings.getInstance(project).canFormat(project, file)
+ }
+ }
}
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 9a2ea95..47b6596 100644
--- a/src/main/kotlin/com/github/biomejs/intellijbiome/services/BiomeServerService.kt
+++ b/src/main/kotlin/com/github/biomejs/intellijbiome/services/BiomeServerService.kt
@@ -1,20 +1,23 @@
package com.github.biomejs.intellijbiome.services
-import com.intellij.openapi.components.Service
-import com.intellij.openapi.project.Project
import com.github.biomejs.intellijbiome.BiomeBundle
import com.github.biomejs.intellijbiome.lsp.BiomeLspServerSupportProvider
import com.intellij.notification.NotificationGroupManager
import com.intellij.notification.NotificationType
+import com.intellij.openapi.components.Service
+import com.intellij.openapi.project.Project
import com.intellij.platform.lsp.api.LspServerManager
@Service(Service.Level.PROJECT)
class BiomeServerService(private val project: Project) {
-
fun restartBiomeServer() {
LspServerManager.getInstance(project).stopAndRestartIfNeeded(BiomeLspServerSupportProvider::class.java)
}
+ fun stopBiomeServer() {
+ LspServerManager.getInstance(project).stopServers(BiomeLspServerSupportProvider::class.java)
+ }
+
fun notifyRestart() {
NotificationGroupManager.getInstance()
.getNotificationGroup("Biome")
diff --git a/src/main/kotlin/com/github/biomejs/intellijbiome/settings/BiomeConfigurable.kt b/src/main/kotlin/com/github/biomejs/intellijbiome/settings/BiomeConfigurable.kt
new file mode 100644
index 0000000..01844de
--- /dev/null
+++ b/src/main/kotlin/com/github/biomejs/intellijbiome/settings/BiomeConfigurable.kt
@@ -0,0 +1,257 @@
+package com.github.biomejs.intellijbiome.settings
+
+import com.github.biomejs.intellijbiome.BiomeBundle
+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
+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
+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.nio.file.FileSystems
+import java.util.regex.PatternSyntaxException
+import javax.swing.JCheckBox
+import javax.swing.JRadioButton
+import javax.swing.text.JTextComponent
+
+private const val CONFIGURABLE_ID = "settings.biome"
+private const val HELP_TOPIC = "reference.settings.biome"
+
+class BiomeConfigurable(internal val project: Project) :
+ BoundSearchableConfigurable(
+ BiomeBundle.message("biome.settings.name"),
+ HELP_TOPIC,
+ CONFIGURABLE_ID
+ ) {
+ lateinit var runFormatOnSaveCheckBox: JCheckBox
+ lateinit var runSafeFixesOnSaveCheckBox: JCheckBox
+ lateinit var runUnsafeFixesOnSaveCheckBox: JCheckBox
+
+ private lateinit var disabledConfiguration: JRadioButton
+ private lateinit var automaticConfiguration: JRadioButton
+ private lateinit var manualConfiguration: JRadioButton
+ override fun createPanel(): DialogPanel {
+ val settings: BiomeSettings = BiomeSettings.getInstance(project)
+ val biomeServerService = project.service()
+
+ return panel {
+ buttonsGroup {
+ row {
+ disabledConfiguration =
+ radioButton(
+ JavaScriptBundle.message(
+ "settings.javascript.linters.autodetect.disabled",
+ displayName
+ )
+ )
+ .bindSelected(ConfigurationModeProperty(settings, ConfigurationMode.DISABLED))
+ .component
+ }
+ 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,
+ "biome.json"
+ )
+
+ val helpLabel = ContextHelpLabel.create(detectAutomaticallyHelpText)
+ helpLabel.border = JBUI.Borders.emptyLeft(UIUtil.DEFAULT_HGAP)
+ cell(helpLabel)
+ }
+ row {
+ manualConfiguration =
+ radioButton(
+ JavaScriptBundle.message(
+ "settings.javascript.linters.autodetect.configure.manually",
+ displayName
+ )
+ )
+ .bindSelected(ConfigurationModeProperty(settings, ConfigurationMode.MANUAL))
+ .component
+ }
+ }
+ panel {
+ row(BiomeBundle.message("biome.path.label")) {
+ textFieldWithBrowseButton(BiomeBundle.message("biome.path.label")) { fileChosen(it) }
+ .bindText(settings::executablePath)
+ }.visibleIf(manualConfiguration.selected)
+
+ row(BiomeBundle.message("biome.config.path.label")) {
+ textFieldWithBrowseButton(BiomeBundle.message("biome.config.path.label")) { fileChosen(it) }
+ .bindText(settings::configPath)
+ }.visibleIf(manualConfiguration.selected)
+ }
+
+ // *********************
+ // Lint pattern row
+ // *********************
+ row(BiomeBundle.message("biome.run.lint.for.files.label")) {
+ textField()
+ .comment(BiomeBundle.message("biome.files.pattern.comment"))
+ .align(AlignX.FILL)
+ .bind(
+ { textField -> textField.text.trim() },
+ JTextComponent::setText,
+ MutableProperty({ settings.lintFilePattern }, { settings.lintFilePattern = it })
+ )
+ .validationOnInput(validateGlob())
+ .component
+ }.enabledIf(!disabledConfiguration.selected)
+
+ // *********************
+ // Format pattern row
+ // *********************
+ row(BiomeBundle.message("biome.run.format.for.files.label")) {
+ textField()
+ .align(AlignX.FILL)
+ .bind(
+ { textField -> textField.text.trim() },
+ JTextComponent::setText,
+ MutableProperty({ settings.formatFilePattern }, { settings.formatFilePattern = it })
+ )
+ .validationOnInput(validateGlob())
+ .component
+ }.enabledIf(!disabledConfiguration.selected)
+
+
+ // *********************
+ // 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
+
+ val link = ActionsOnSaveConfigurable.createGoToActionsOnSavePageLink()
+ cell(link)
+ }.enabledIf(!disabledConfiguration.selected)
+
+ // *********************
+ // Apply safe fixes on save row
+ // *********************
+ row {
+ runSafeFixesOnSaveCheckBox = checkBox(BiomeBundle.message("biome.run.safe.fixes.on.save.label"))
+ .bindSelected(RunOnObservableProperty(
+ { settings.configurationMode != ConfigurationMode.DISABLED && settings.applySafeFixesOnSave },
+ { settings.applySafeFixesOnSave = it },
+ { !disabledConfiguration.isSelected && runSafeFixesOnSaveCheckBox.isSelected }
+ ))
+ .component
+
+ val link = ActionsOnSaveConfigurable.createGoToActionsOnSavePageLink()
+ cell(link)
+ }.enabledIf(!disabledConfiguration.selected)
+
+
+ // *********************
+ // Apply unsafe fixes 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
+
+ val link = ActionsOnSaveConfigurable.createGoToActionsOnSavePageLink()
+ cell(link)
+ }.enabledIf(!disabledConfiguration.selected)
+
+ onApply {
+ biomeServerService.restartBiomeServer()
+ 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 fileChosen(file: VirtualFile): String {
+ return file.path
+ }
+
+ private class ConfigurationModeProperty(
+ private val settings: BiomeSettings,
+ private val mode: ConfigurationMode
+ ) : MutableProperty {
+ override fun get(): Boolean =
+ settings.configurationMode == mode
+
+ override fun set(value: Boolean) {
+ if (value) {
+ settings.configurationMode = mode
+ }
+ }
+ }
+
+ 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
new file mode 100644
index 0000000..dca2403
--- /dev/null
+++ b/src/main/kotlin/com/github/biomejs/intellijbiome/settings/BiomeOnSaveApplySafeFixesActionInfo.kt
@@ -0,0 +1,27 @@
+package com.github.biomejs.intellijbiome.settings
+
+import com.github.biomejs.intellijbiome.BiomeBundle
+import com.intellij.ide.actionsOnSave.ActionOnSaveBackedByOwnConfigurable
+import com.intellij.ide.actionsOnSave.ActionOnSaveContext
+
+class BiomeOnSaveApplySafeFixesActionInfo(actionOnSaveContext: ActionOnSaveContext) :
+ ActionOnSaveBackedByOwnConfigurable(
+ actionOnSaveContext,
+ BiomeConfigurable.CONFIGURABLE_ID,
+ BiomeConfigurable::class.java
+ ) {
+
+ override fun getActionOnSaveName() =
+ BiomeBundle.message("biome.run.safe.fixes.on.save.checkbox.on.actions.on.save.page")
+
+ override fun isActionOnSaveEnabledAccordingToStoredState() = BiomeSettings.getInstance(project).applySafeFixesOnSave
+
+ override fun isActionOnSaveEnabledAccordingToUiState(configurable: BiomeConfigurable) =
+ configurable.runSafeFixesOnSaveCheckBox.isSelected
+
+ override fun setActionOnSaveEnabled(configurable: BiomeConfigurable, enabled: Boolean) {
+ configurable.runSafeFixesOnSaveCheckBox.isSelected = enabled
+ }
+
+ override fun getActionLinks() = listOf(createGoToPageInSettingsLink(BiomeConfigurable.CONFIGURABLE_ID))
+}
diff --git a/src/main/kotlin/com/github/biomejs/intellijbiome/settings/BiomeOnSaveApplyUnsafeFixesActionInfo.kt b/src/main/kotlin/com/github/biomejs/intellijbiome/settings/BiomeOnSaveApplyUnsafeFixesActionInfo.kt
new file mode 100644
index 0000000..180c7eb
--- /dev/null
+++ b/src/main/kotlin/com/github/biomejs/intellijbiome/settings/BiomeOnSaveApplyUnsafeFixesActionInfo.kt
@@ -0,0 +1,28 @@
+package com.github.biomejs.intellijbiome.settings
+
+import com.github.biomejs.intellijbiome.BiomeBundle
+import com.intellij.ide.actionsOnSave.ActionOnSaveBackedByOwnConfigurable
+import com.intellij.ide.actionsOnSave.ActionOnSaveContext
+
+class BiomeOnSaveApplyUnsafeFixesActionInfo(actionOnSaveContext: ActionOnSaveContext) :
+ ActionOnSaveBackedByOwnConfigurable(
+ actionOnSaveContext,
+ BiomeConfigurable.CONFIGURABLE_ID,
+ BiomeConfigurable::class.java
+ ) {
+
+ override fun getActionOnSaveName() =
+ BiomeBundle.message("biome.run.unsafe.fixes.on.save.checkbox.on.actions.on.save.page")
+
+ override fun isActionOnSaveEnabledAccordingToStoredState() =
+ BiomeSettings.getInstance(project).applyUnsafeFixesOnSave
+
+ override fun isActionOnSaveEnabledAccordingToUiState(configurable: BiomeConfigurable) =
+ configurable.runUnsafeFixesOnSaveCheckBox.isSelected
+
+ override fun setActionOnSaveEnabled(configurable: BiomeConfigurable, enabled: Boolean) {
+ configurable.runUnsafeFixesOnSaveCheckBox.isSelected = enabled
+ }
+
+ 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
new file mode 100644
index 0000000..7f7dad8
--- /dev/null
+++ b/src/main/kotlin/com/github/biomejs/intellijbiome/settings/BiomeOnSaveFormatActionInfo.kt
@@ -0,0 +1,27 @@
+package com.github.biomejs.intellijbiome.settings
+
+import com.github.biomejs.intellijbiome.BiomeBundle
+import com.intellij.ide.actionsOnSave.ActionOnSaveBackedByOwnConfigurable
+import com.intellij.ide.actionsOnSave.ActionOnSaveContext
+
+class BiomeOnSaveFormatActionInfo(actionOnSaveContext: ActionOnSaveContext) :
+ ActionOnSaveBackedByOwnConfigurable(
+ actionOnSaveContext,
+ BiomeConfigurable.CONFIGURABLE_ID,
+ BiomeConfigurable::class.java
+ ) {
+
+ override fun getActionOnSaveName() =
+ BiomeBundle.message("biome.run.format.on.save.checkbox.on.actions.on.save.page")
+
+ override fun isActionOnSaveEnabledAccordingToStoredState() = BiomeSettings.getInstance(project).formatOnSave
+
+ override fun isActionOnSaveEnabledAccordingToUiState(configurable: BiomeConfigurable) =
+ configurable.runFormatOnSaveCheckBox.isSelected
+
+ override fun setActionOnSaveEnabled(configurable: BiomeConfigurable, enabled: Boolean) {
+ configurable.runFormatOnSaveCheckBox.isSelected = enabled
+ }
+
+ override fun getActionLinks() = listOf(createGoToPageInSettingsLink(BiomeConfigurable.CONFIGURABLE_ID))
+}
diff --git a/src/main/kotlin/com/github/biomejs/intellijbiome/settings/BiomeOnSaveInfoProvider.kt b/src/main/kotlin/com/github/biomejs/intellijbiome/settings/BiomeOnSaveInfoProvider.kt
new file mode 100644
index 0000000..a3a7045
--- /dev/null
+++ b/src/main/kotlin/com/github/biomejs/intellijbiome/settings/BiomeOnSaveInfoProvider.kt
@@ -0,0 +1,24 @@
+package com.github.biomejs.intellijbiome.settings
+
+import com.github.biomejs.intellijbiome.BiomeBundle
+import com.intellij.ide.actionsOnSave.ActionOnSaveContext
+import com.intellij.ide.actionsOnSave.ActionOnSaveInfo
+import com.intellij.ide.actionsOnSave.ActionOnSaveInfoProvider
+
+class BiomeOnSaveInfoProvider : ActionOnSaveInfoProvider() {
+ override fun getActionOnSaveInfos(context: ActionOnSaveContext):
+ List = listOf(
+ BiomeOnSaveFormatActionInfo(context),
+ BiomeOnSaveApplySafeFixesActionInfo(context),
+ BiomeOnSaveApplyUnsafeFixesActionInfo(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")
+ )
+ }
+}
+
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 e6d3db1..13ea298 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,10 @@
package com.github.biomejs.intellijbiome.settings
+import com.intellij.lang.javascript.linter.GlobPatternUtil
import com.intellij.openapi.components.*
import com.intellij.openapi.project.Project
+import com.intellij.openapi.vfs.VirtualFile
+
@Service(Service.Level.PROJECT)
@State(name = "BiomeSettings", storages = [(Storage("biome.xml"))])
@@ -18,6 +21,52 @@ class BiomeSettings :
state.configPath = value
}
+ var formatFilePattern: String
+ get() = state.formatFilePattern ?: BiomeSettingsState.DEFAULT_FILE_PATTERN
+ set(value) {
+ state.formatFilePattern = value
+ }
+
+ var lintFilePattern: String
+ get() = state.lintFilePattern ?: BiomeSettingsState.DEFAULT_FILE_PATTERN
+ set(value) {
+ state.lintFilePattern = value
+ }
+
+ var configurationMode: ConfigurationMode
+ get() = state.configurationMode
+ set(value) {
+ state.configurationMode = value
+ }
+
+ var formatOnSave: Boolean
+ get() = isEnabled() && state.formatOnSave
+ set(value) {
+ state.formatOnSave = value
+ }
+
+ var applySafeFixesOnSave: Boolean
+ get() = isEnabled() && state.applySafeFixesOnSave
+ set(value) {
+ state.applySafeFixesOnSave = value
+ }
+
+ var applyUnsafeFixesOnSave: Boolean
+ get() = isEnabled() && state.applyUnsafeFixesOnSave
+ set(value) {
+ state.applyUnsafeFixesOnSave = value
+ }
+
+ fun isEnabled(): Boolean {
+ return configurationMode !== ConfigurationMode.DISABLED
+ }
+
+ fun canFormat(project: Project, file: VirtualFile): Boolean =
+ GlobPatternUtil.isFileMatchingGlobPattern(project, formatFilePattern, file)
+
+ fun canLint(project: Project, file: VirtualFile): Boolean =
+ GlobPatternUtil.isFileMatchingGlobPattern(project, lintFilePattern, file)
+
companion object {
@JvmStatic
fun getInstance(project: Project): BiomeSettings = project.service()
diff --git a/src/main/kotlin/com/github/biomejs/intellijbiome/settings/BiomeSettingsConfigurable.kt b/src/main/kotlin/com/github/biomejs/intellijbiome/settings/BiomeSettingsConfigurable.kt
deleted file mode 100644
index d24a146..0000000
--- a/src/main/kotlin/com/github/biomejs/intellijbiome/settings/BiomeSettingsConfigurable.kt
+++ /dev/null
@@ -1,44 +0,0 @@
-package com.github.biomejs.intellijbiome.settings
-
-import com.github.biomejs.intellijbiome.BiomeBundle
-import com.github.biomejs.intellijbiome.services.BiomeServerService
-import com.intellij.openapi.components.service
-import com.intellij.openapi.options.BoundSearchableConfigurable
-import com.intellij.openapi.project.Project
-import com.intellij.openapi.ui.DialogPanel
-import com.intellij.openapi.vfs.VirtualFile
-import com.intellij.ui.dsl.builder.bindText
-import com.intellij.ui.dsl.builder.panel
-
-class BiomeSettingsConfigurable(internal val project: Project) :
- BoundSearchableConfigurable(
- BiomeBundle.message("biome.settings.name"),
- BiomeBundle.message("biome.settings.name")
- ) {
- override fun createPanel(): DialogPanel {
- val settings: BiomeSettings = BiomeSettings.getInstance(project)
- val biomeServerService = project.service()
-
- return panel {
- row(BiomeBundle.message("biome.path.label")) {
- textFieldWithBrowseButton(BiomeBundle.message("biome.path.label")) { fileChosen(it) }
- .bindText(settings::executablePath)
- }
-
- row(BiomeBundle.message("biome.config.path.label")) {
- textFieldWithBrowseButton(BiomeBundle.message("biome.config.path.label")) { fileChosen(it) }
- .bindText(settings::configPath)
- }
-
- onApply {
- biomeServerService.restartBiomeServer()
- biomeServerService.notifyRestart()
- }
- }
-
- }
-
- fun fileChosen(file: VirtualFile): String {
- return file.path
- }
-}
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 ac2c6bc..48fda07 100644
--- a/src/main/kotlin/com/github/biomejs/intellijbiome/settings/BiomeSettingsState.kt
+++ b/src/main/kotlin/com/github/biomejs/intellijbiome/settings/BiomeSettingsState.kt
@@ -4,9 +4,26 @@ import com.intellij.openapi.components.BaseState
import com.intellij.openapi.components.Service
import org.jetbrains.annotations.ApiStatus
+@ApiStatus.Internal
+enum class ConfigurationMode {
+ DISABLED,
+ AUTOMATIC,
+ MANUAL
+}
+
@Service
@ApiStatus.Internal
class BiomeSettingsState : BaseState() {
var executablePath by string()
var configPath by string()
+ var formatFilePattern by string(DEFAULT_FILE_PATTERN)
+ var lintFilePattern by string(DEFAULT_FILE_PATTERN)
+ var formatOnSave by property(false)
+ var applySafeFixesOnSave by property(false)
+ var applyUnsafeFixesOnSave by property(false)
+ var configurationMode by enum(ConfigurationMode.AUTOMATIC)
+
+ companion object {
+ const val DEFAULT_FILE_PATTERN = "**/*.{js,mjs,cjs,ts,jsx,tsx,cts,json,jsonc}"
+ }
}
diff --git a/src/main/kotlin/com/github/biomejs/intellijbiome/widgets/BiomeWidget.kt b/src/main/kotlin/com/github/biomejs/intellijbiome/widgets/BiomeWidget.kt
index 0d674ff..c64a1a0 100644
--- a/src/main/kotlin/com/github/biomejs/intellijbiome/widgets/BiomeWidget.kt
+++ b/src/main/kotlin/com/github/biomejs/intellijbiome/widgets/BiomeWidget.kt
@@ -1,24 +1,19 @@
package com.github.biomejs.intellijbiome.widgets
import com.github.biomejs.intellijbiome.BiomeBundle
-import com.github.biomejs.intellijbiome.BiomeUtils
+import com.github.biomejs.intellijbiome.BiomePackage
import com.github.biomejs.intellijbiome.listeners.BIOME_CONFIG_RESOLVED_TOPIC
import com.github.biomejs.intellijbiome.listeners.BiomeConfigResolvedListener
import com.github.biomejs.intellijbiome.lsp.BiomeLspServerSupportProvider
-import com.intellij.openapi.application.ApplicationManager
-import com.intellij.openapi.application.ModalityState
+import com.github.biomejs.intellijbiome.settings.BiomeSettings
+import com.intellij.openapi.diagnostic.Logger
+import com.intellij.openapi.progress.ProgressManager
import com.intellij.openapi.project.Project
-import com.intellij.openapi.wm.CustomStatusBarWidget
+import com.intellij.openapi.wm.StatusBarWidget
import com.intellij.openapi.wm.StatusBarWidget.WidgetPresentation
-import com.intellij.openapi.wm.WindowManager
import com.intellij.openapi.wm.impl.status.EditorBasedWidget
-import com.intellij.openapi.wm.impl.status.TextPanel.WithIconAndArrows
import com.intellij.platform.lsp.api.LspServerManager
import com.intellij.platform.lsp.impl.LspServerImpl
-import javax.swing.JComponent
-import com.intellij.openapi.diagnostic.Logger
-import com.intellij.openapi.progress.ProgressManager
-import com.intellij.openapi.wm.StatusBarWidget
class BiomeWidget(project: Project) : EditorBasedWidget(project), StatusBarWidget,
StatusBarWidget.MultipleTextValuesPresentation {
@@ -36,7 +31,7 @@ class BiomeWidget(project: Project) : EditorBasedWidget(project), StatusBarWidge
}
override fun ID(): String {
- return javaClass.name;
+ return javaClass.name
}
override fun getPresentation(): WidgetPresentation {
@@ -44,19 +39,22 @@ class BiomeWidget(project: Project) : EditorBasedWidget(project), StatusBarWidge
}
override fun getSelectedValue(): String? {
- val biomeBin = BiomeUtils.getBiomeExecutablePath(project);
- val progressManager = ProgressManager.getInstance()
-
- if (biomeBin == null) {
- return "Biome"
+ val settings = BiomeSettings.getInstance(project)
+ if (!settings.isEnabled()) {
+ return null
}
+ val binary = BiomePackage.binaryPath(project)
+ val progressManager = ProgressManager.getInstance()
val version = progressManager.runProcessWithProgressSynchronously({
- BiomeUtils.getBiomeVersion(project, biomeBin)
+ BiomePackage.versionNumber(project, binary)
}, BiomeBundle.message("biome.loading"), true, project)
+ if (version.isNullOrEmpty()) {
+ return null
+ }
- return "Biome ${version}"
+ return BiomeBundle.message("biome.widget.version", version)
}
override fun getTooltipText(): String {
diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml
index 00ba892..80e3813 100644
--- a/src/main/resources/META-INF/plugin.xml
+++ b/src/main/resources/META-INF/plugin.xml
@@ -1,9 +1,9 @@
- com.github.biomejs.intellijbiome
- Biome
- biomejs
- com.github.biomejs.intellijbiome
+ Biome
+ biomejs
+ Biome plugin for JetBrains IDEs.
Features
@@ -16,43 +16,62 @@
]]>
- com.intellij.modules.platform
- com.intellij.modules.ultimate
- JavaScript
+ com.intellij.modules.platform
+ com.intellij.modules.ultimate
+ JavaScript
- messages.BiomeBundle
+ messages.BiomeBundle
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/resources/META-INF/pluginIcon.svg b/src/main/resources/META-INF/pluginIcon.svg
index 3296987..3347d37 100644
--- a/src/main/resources/META-INF/pluginIcon.svg
+++ b/src/main/resources/META-INF/pluginIcon.svg
@@ -1,4 +1,5 @@
diff --git a/src/main/resources/icons/pluginIcon.svg b/src/main/resources/icons/pluginIcon.svg
index bed6c6e..3347d37 100644
--- a/src/main/resources/icons/pluginIcon.svg
+++ b/src/main/resources/icons/pluginIcon.svg
@@ -1,3 +1,5 @@
diff --git a/src/main/resources/messages/BiomeBundle.properties b/src/main/resources/messages/BiomeBundle.properties
index 4a63296..9072d93 100644
--- a/src/main/resources/messages/BiomeBundle.properties
+++ b/src/main/resources/messages/BiomeBundle.properties
@@ -1,7 +1,8 @@
name=Biome
+biome.formatting.service.name=Biome
+biome.settings.name=Biome
biome.path.label=Biome CLI Path
-biome.config.path.label=biome.json Directory Path
-biome.settings.name=Biome Settings
+biome.config.path.label=Directory path of biome.json
biome.formatting.failure=Formatting error
biome.language.server.not.found=Biome language server is not found, make sure you have @biomejs/biome installed.
biome.loading=Biome is loading...
@@ -9,3 +10,20 @@ biome.language.server.is.running=Biome server is running
biome.language.server.is.stopped=Biome server is stopped
biome.language.server.restarted=Biome server restarted
biome.interpreter.not.configured=Your node interpreter not configured.
+biome.invalid.pattern=Invalid pattern
+biome.run.format.for.files.label=Run format for &files:
+biome.run.lint.for.files.label=Run lint for &files:
+biome.run.format.on.save.label=Run format on &save
+biome.run.safe.fixes.on.save.label=Run safe fixes on &save
+biome.run.unsafe.fixes.on.save.label=Run unsafe fixes on &save
+biome.files.pattern.comment=Use a glob pattern, for example, **/*.{js,ts}
+biome.run.format.on.save.checkbox.on.actions.on.save.page=Run Biome format
+biome.run.safe.fixes.on.save.checkbox.on.actions.on.save.page=Run Biome lint --apply
+biome.run.unsafe.fixes.on.save.checkbox.on.actions.on.save.page=Run Biome lint --apply-unsafe
+action.ReformatWithBiomeAction.text=Reformat with Biome
+action.ReformatWithBiomeAction.description=Reformat with Biome
+biome.apply.safe.fix.with.biome=Apply safe fixes with Biome
+biome.apply.unsafe.fix.with.biome=Apply unsafe fixes with Biome
+biome.failed.to.format.file=Biome failed to format the file
+biome.failed.to.fix.file=Biome failed to fix the file
+biome.widget.version=Biome {0}
diff --git a/src/test/kotlin/com/github/biomejs/intellijbiome/BasicProjectNpmTest.kt b/src/test/kotlin/com/github/biomejs/intellijbiome/BasicProjectNpmTest.kt
index ed63fc2..f3c1cc9 100644
--- a/src/test/kotlin/com/github/biomejs/intellijbiome/BasicProjectNpmTest.kt
+++ b/src/test/kotlin/com/github/biomejs/intellijbiome/BasicProjectNpmTest.kt
@@ -4,27 +4,25 @@ import com.github.biomejs.intellijbiome.pages.*
import com.github.biomejs.intellijbiome.utils.RemoteRobotExtension
import com.github.biomejs.intellijbiome.utils.StepsLogger
import com.intellij.remoterobot.RemoteRobot
-import com.intellij.remoterobot.fixtures.ComponentFixture
import com.intellij.remoterobot.stepsProcessing.step
import com.intellij.remoterobot.utils.keyboard
import com.intellij.remoterobot.utils.waitFor
import com.intellij.remoterobot.utils.waitForIgnoringError
-import org.junit.jupiter.api.AfterEach
-import org.junit.jupiter.api.Assertions.assertEquals
+import org.junit.jupiter.api.AfterAll
import org.junit.jupiter.api.Assertions.assertTrue
+import org.junit.jupiter.api.BeforeAll
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import java.awt.Point
import java.awt.event.KeyEvent.*
import java.io.File
-import java.time.Duration
import java.time.Duration.ofMinutes
@ExtendWith(RemoteRobotExtension::class)
class BasicProjectNpmTest {
- private val basicProjectPath = File("src/test/testData/basic-project")
+
init {
StepsLogger.init()
@@ -35,51 +33,30 @@ class BasicProjectNpmTest {
waitForIgnoringError(ofMinutes(3)) { remoteRobot.callJs("true") }
}
- @AfterEach
- fun closeProject(remoteRobot: RemoteRobot) = with(remoteRobot) {
- idea {
- if (remoteRobot.isMac()) {
- keyboard {
- hotKey(VK_SHIFT, VK_META, VK_A)
- enterText("Close Project", 20)
- enter()
- }
- } else {
- menuBar.select("File", "Close Project")
- }
- }
- }
-
@Test
fun openQuickFixes(remoteRobot: RemoteRobot) = with(remoteRobot) {
- welcomeFrame {
- openProjectLink.click()
- dialog("Open File or Project") {
- directoryPath.text = basicProjectPath.absolutePath
- button("OK").click()
- }
- }
-
idea {
step("Check biome running version") {
waitFor(ofMinutes(5)) { isDumbMode().not() }
- openFile("index.js")
-
- val editor = editor("index.js")
-
- editor.click(Point(0, 0))
-
- keyboard {
- hotKey(VK_ALT, VK_ENTER)
+ step("Open file") {
+ openFile("index.js")
+ val editor = editor("index.js")
+ editor.click(Point(0, 0))
}
- quickfix {
- val items = collectItems()
+ step("Open quickfixes dialog") {
+ keyboard {
+ hotKey(VK_ALT, VK_ENTER)
+ }
+ quickfix {
+ val items = collectItems()
+
- assertTrue(items.contains("Use 'const' instead."))
- assertTrue(items.contains("Suppress rule lint/style/noVar"))
+ assertTrue(items.contains("Use 'const' instead."))
+ assertTrue(items.contains("Suppress rule lint/style/noVar"))
+ }
}
keyboard {
@@ -89,4 +66,28 @@ class BasicProjectNpmTest {
}
}
+
+ companion object {
+ private val basicProjectPath = File("src/test/testData/basic-project")
+
+ @JvmStatic
+ @BeforeAll
+ fun selectProject(remoteRobot: RemoteRobot) = with(remoteRobot) {
+ welcomeFrame {
+ openProjectLink.click()
+ dialog("Open File or Project") {
+ directoryPath.text = basicProjectPath.absolutePath
+ button("OK").click()
+ }
+ }
+ }
+
+ @JvmStatic
+ @AfterAll
+ fun closeProject(remoteRobot: RemoteRobot) = with(remoteRobot) {
+ idea {
+ menuBar.select("File", "Close Project")
+ }
+ }
+ }
}
diff --git a/src/test/kotlin/com/github/biomejs/intellijbiome/pages/DialogFixture.kt b/src/test/kotlin/com/github/biomejs/intellijbiome/pages/DialogFixture.kt
index 9f99668..3e709de 100644
--- a/src/test/kotlin/com/github/biomejs/intellijbiome/pages/DialogFixture.kt
+++ b/src/test/kotlin/com/github/biomejs/intellijbiome/pages/DialogFixture.kt
@@ -10,22 +10,24 @@ import com.intellij.remoterobot.stepsProcessing.step
import java.time.Duration
fun ContainerFixture.dialog(
- title: String,
- timeout: Duration = Duration.ofSeconds(20),
- function: DialogFixture.() -> Unit = {}): DialogFixture = step("Search for dialog with title $title") {
- find(DialogFixture.byTitle(title), timeout).apply(function)
+ title: String,
+ timeout: Duration = Duration.ofSeconds(20),
+ function: DialogFixture.() -> Unit = {}
+): DialogFixture = step("Search for dialog with title $title") {
+ find(DialogFixture.byTitle(title), timeout).apply(function)
}
@FixtureName("Dialog")
class DialogFixture(
- remoteRobot: RemoteRobot,
- remoteComponent: RemoteComponent) : CommonContainerFixture(remoteRobot, remoteComponent) {
+ remoteRobot: RemoteRobot,
+ remoteComponent: RemoteComponent
+) : CommonContainerFixture(remoteRobot, remoteComponent) {
- companion object {
- @JvmStatic
- fun byTitle(title: String) = byXpath("title $title", "//div[@title='$title' and @class='MyDialog']")
- }
+ companion object {
+ @JvmStatic
+ fun byTitle(title: String) = byXpath("title $title", "//div[@title='$title' and @class='MyDialog']")
+ }
- val title: String
- get() = callJs("component.getTitle();")
+ val title: String
+ get() = callJs("component.getTitle();")
}
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 cdcc278..b109a8b 100644
--- a/src/test/kotlin/com/github/biomejs/intellijbiome/pages/Editor.kt
+++ b/src/test/kotlin/com/github/biomejs/intellijbiome/pages/Editor.kt
@@ -7,7 +7,6 @@ import com.intellij.remoterobot.fixtures.ComponentFixture
import com.intellij.remoterobot.fixtures.ContainerFixture
import com.intellij.remoterobot.fixtures.FixtureName
import com.intellij.remoterobot.search.locators.byXpath
-import java.awt.Point
@JvmOverloads
fun ContainerFixture.editor(title: String, function: Editor.() -> Unit = {}): Editor {
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 e22c8f4..e5245a1 100644
--- a/src/test/kotlin/com/github/biomejs/intellijbiome/pages/IdeaFrame.kt
+++ b/src/test/kotlin/com/github/biomejs/intellijbiome/pages/IdeaFrame.kt
@@ -2,42 +2,45 @@ package com.github.biomejs.intellijbiome.pages
import com.intellij.remoterobot.RemoteRobot
import com.intellij.remoterobot.data.RemoteComponent
-import com.intellij.remoterobot.fixtures.*
+import com.intellij.remoterobot.fixtures.CommonContainerFixture
+import com.intellij.remoterobot.fixtures.DefaultXpath
+import com.intellij.remoterobot.fixtures.FixtureName
+import com.intellij.remoterobot.fixtures.JMenuBarFixture
import com.intellij.remoterobot.stepsProcessing.step
import com.intellij.remoterobot.utils.waitFor
import java.time.Duration
fun RemoteRobot.idea(function: IdeaFrame.() -> Unit) {
- find(timeout = Duration.ofSeconds(10)).apply(function)
+ find(timeout = Duration.ofSeconds(10)).apply(function)
}
@FixtureName("Idea frame")
@DefaultXpath("IdeFrameImpl type", "//div[@class='IdeFrameImpl']")
class IdeaFrame(remoteRobot: RemoteRobot, remoteComponent: RemoteComponent) :
- CommonContainerFixture(remoteRobot, remoteComponent) {
- val menuBar: JMenuBarFixture
- get() = step("Menu...") {
- return@step remoteRobot.find(JMenuBarFixture::class.java, JMenuBarFixture.byType())
- }
+ CommonContainerFixture(remoteRobot, remoteComponent) {
+ val menuBar: JMenuBarFixture
+ get() = step("Menu...") {
+ return@step remoteRobot.find(JMenuBarFixture::class.java, JMenuBarFixture.byType())
+ }
- @JvmOverloads
- 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)
- }
- function()
- step("..wait for smart mode again") {
- waitFor(duration = timeout, interval = Duration.ofSeconds(5)) {
- isDumbMode().not()
- }
- }
- }
- }
+ @JvmOverloads
+ 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)
+ }
+ function()
+ step("..wait for smart mode again") {
+ waitFor(duration = timeout, interval = Duration.ofSeconds(5)) {
+ isDumbMode().not()
+ }
+ }
+ }
+ }
- fun isDumbMode(): Boolean {
- return callJs(
- """
+ fun isDumbMode(): Boolean {
+ return callJs(
+ """
const frameHelper = com.intellij.openapi.wm.impl.ProjectFrameHelper.getFrameHelper(component)
if (frameHelper) {
const project = frameHelper.getProject()
@@ -46,12 +49,12 @@ class IdeaFrame(remoteRobot: RemoteRobot, remoteComponent: RemoteComponent) :
true
}
""", true
- )
- }
+ )
+ }
- fun openFile(path: String) {
- runJs(
- """
+ fun openFile(path: String) {
+ runJs(
+ """
importPackage(com.intellij.openapi.fileEditor)
importPackage(com.intellij.openapi.vfs)
importPackage(com.intellij.openapi.wm.impl)
@@ -70,6 +73,6 @@ class IdeaFrame(remoteRobot: RemoteRobot, remoteComponent: RemoteComponent) :
)
}
""", true
- )
- }
+ )
+ }
}
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 f6921e8..a25806c 100644
--- a/src/test/kotlin/com/github/biomejs/intellijbiome/pages/StatusBar.kt
+++ b/src/test/kotlin/com/github/biomejs/intellijbiome/pages/StatusBar.kt
@@ -2,22 +2,33 @@ package com.github.biomejs.intellijbiome.pages
import com.intellij.remoterobot.RemoteRobot
import com.intellij.remoterobot.data.RemoteComponent
-import com.intellij.remoterobot.fixtures.*
+import com.intellij.remoterobot.fixtures.CommonContainerFixture
+import com.intellij.remoterobot.fixtures.ContainerFixture
+import com.intellij.remoterobot.fixtures.DefaultXpath
+import com.intellij.remoterobot.fixtures.FixtureName
import com.intellij.remoterobot.search.locators.byXpath
-import com.intellij.remoterobot.utils.waitFor
import java.time.Duration
+
fun RemoteRobot.statusBar(function: StatusbarFrame.() -> Unit) {
- find(timeout = Duration.ofSeconds(10)).apply(function)
+ find(timeout = Duration.ofSeconds(10)).apply(function)
}
@FixtureName("Statusbar frame")
@DefaultXpath("IdeStatusBarImpl type", "//div[@class='IdeStatusBarImpl']")
class StatusbarFrame(remoteRobot: RemoteRobot, remoteComponent: RemoteComponent) :
- CommonContainerFixture(remoteRobot, remoteComponent) {
+ CommonContainerFixture(remoteRobot, remoteComponent) {
+
+ val statusBarPanel
+ get() = find(
+ byXpath(
+ "StatusBarPanel",
+ "//div[@class='StatusBarPanel'][.//div[@class='CodeStyleStatusBarPanel']]"
+ )
+ )
- val statusBarPanel get() = find(byXpath("StatusBarPanel", "//div[@class='StatusBarPanel'][.//div[@class='CodeStyleStatusBarPanel']]"))
- fun byContainsText(text: String) = byXpath("text $text", "//div[contains(@text,'$text') and @class='WithIconAndArrows']")
+ fun byContainsText(text: String) =
+ byXpath("text $text", "//div[contains(@text,'$text') and @class='WithIconAndArrows']")
- val text: String
- get() = callJs("component.getText();")
+ val text: String
+ get() = callJs("component.getText();")
}
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 e353c07..0d55130 100644
--- a/src/test/kotlin/com/github/biomejs/intellijbiome/pages/WelcomeFrame.kt
+++ b/src/test/kotlin/com/github/biomejs/intellijbiome/pages/WelcomeFrame.kt
@@ -2,7 +2,9 @@ package com.github.biomejs.intellijbiome.pages
import com.intellij.remoterobot.RemoteRobot
import com.intellij.remoterobot.data.RemoteComponent
-import com.intellij.remoterobot.fixtures.*
+import com.intellij.remoterobot.fixtures.CommonContainerFixture
+import com.intellij.remoterobot.fixtures.DefaultXpath
+import com.intellij.remoterobot.fixtures.FixtureName
import com.intellij.remoterobot.search.locators.byXpath
import java.time.Duration
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 6cead4e..e9beec0 100644
--- a/src/test/kotlin/com/github/biomejs/intellijbiome/utils/RemoteRobotExtension.kt
+++ b/src/test/kotlin/com/github/biomejs/intellijbiome/utils/RemoteRobotExtension.kt
@@ -13,7 +13,6 @@ import org.junit.jupiter.api.extension.ParameterResolver
import java.awt.image.BufferedImage
import java.io.ByteArrayOutputStream
import java.io.File
-import java.lang.IllegalStateException
import java.lang.reflect.Method
import javax.imageio.ImageIO
diff --git a/src/test/testData/basic-project/package.json b/src/test/testData/basic-project/package.json
index 6b8a5c5..d704829 100644
--- a/src/test/testData/basic-project/package.json
+++ b/src/test/testData/basic-project/package.json
@@ -1,14 +1,14 @@
{
"name": "basic-project",
"version": "1.0.0",
- "private": true,
+ "private": true,
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"license": "MIT OR Apache-2.0",
- "dependencies": {
- "@biomejs/biome": "workspace:*"
- }
+ "dependencies": {
+ "@biomejs/biome": "workspace:*"
+ }
}