├── .node-version
├── plugin-verifier-ignored-problems.txt
├── sandbox
├── oxfmt
│ └── nested-configs
│ │ ├── .oxfmtrc.json
│ │ ├── .gitignore
│ │ ├── index.js
│ │ ├── package.json
│ │ └── package-lock.json
└── oxlint
│ ├── nested-configs
│ ├── .gitignore
│ ├── package-b
│ │ ├── index.js
│ │ └── .oxlintrc.json
│ ├── index.js
│ ├── package-c
│ │ ├── index.js
│ │ └── .oxlintrc.json
│ ├── package-a
│ │ ├── index.js
│ │ ├── test.jsx
│ │ └── .oxlintrc.json
│ ├── package.json
│ ├── .oxlintrc.json
│ └── package-lock.json
│ └── custom-config
│ ├── .gitignore
│ ├── .vscode
│ └── settings.json
│ ├── index.js
│ ├── custom-oxlint.jsonc
│ ├── package.json
│ ├── .idea
│ └── OxcSettings.xml
│ └── package-lock.json
├── .gitignore
├── gradle
├── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
└── libs.versions.toml
├── src
├── test
│ ├── testData
│ │ └── oxlint
│ │ │ └── highlighting
│ │ │ ├── nested-config
│ │ │ ├── subdirectory
│ │ │ │ ├── index.js
│ │ │ │ ├── index.expected.js
│ │ │ │ └── .oxlintrc.json
│ │ │ ├── index.js
│ │ │ ├── package.json
│ │ │ ├── index.expected.js
│ │ │ ├── .oxlintrc.json
│ │ │ └── package-lock.json
│ │ │ ├── no-config
│ │ │ ├── index.js
│ │ │ ├── package.json
│ │ │ ├── index.expected.js
│ │ │ └── package-lock.json
│ │ │ └── custom-config
│ │ │ ├── index.js
│ │ │ ├── package.json
│ │ │ ├── custom-oxlint.jsonc
│ │ │ ├── index.expected.js
│ │ │ └── package-lock.json
│ └── kotlin
│ │ └── com
│ │ └── github
│ │ └── oxc
│ │ └── project
│ │ └── oxcintellijplugin
│ │ ├── oxlint
│ │ ├── NoConfigHighlightingTest.kt
│ │ ├── NestedConfigHighlightingTest.kt
│ │ └── CustomConfigHighlightingTest.kt
│ │ └── extensions
│ │ └── TestCaseExt.kt
└── main
│ ├── kotlin
│ └── com
│ │ └── github
│ │ └── oxc
│ │ └── project
│ │ └── oxcintellijplugin
│ │ ├── Constants.kt
│ │ ├── ConfigurationMode.kt
│ │ ├── OxcIcons.kt
│ │ ├── oxlint
│ │ ├── OxlintRunTrigger.kt
│ │ ├── OxlintUnusedDisableDirectivesSeverity.kt
│ │ ├── OxlintFixKind.kt
│ │ ├── OxlintBundle.kt
│ │ ├── listeners
│ │ │ ├── OxlintEditorWatcher.kt
│ │ │ └── OxlintConfigWatcher.kt
│ │ ├── lsp
│ │ │ ├── OxlintLspDiagnosticsSupport.kt
│ │ │ ├── OxlintLspServerSupportProvider.kt
│ │ │ └── OxlintLspServerDescriptor.kt
│ │ ├── OxlintSchemaProviderFactory.kt
│ │ ├── settings
│ │ │ ├── OxlintSettingsState.kt
│ │ │ ├── OxlintOnSaveFixAllActionInfo.kt
│ │ │ └── OxlintSettings.kt
│ │ ├── actions
│ │ │ ├── OxlintFixAllOnSaveAction.kt
│ │ │ └── OxlintFixAllAction.kt
│ │ ├── services
│ │ │ └── OxlintServerService.kt
│ │ └── OxlintPackage.kt
│ │ ├── ProcessCommandParameter.kt
│ │ ├── ProcessCommandBuilder.kt
│ │ ├── oxfmt
│ │ ├── OxfmtBundle.kt
│ │ ├── lsp
│ │ │ ├── OxfmtLspFormattingSupport.kt
│ │ │ ├── OxfmtLspServerSupportProvider.kt
│ │ │ └── OxfmtLspServerDescriptor.kt
│ │ ├── settings
│ │ │ ├── OxfmtSettingsState.kt
│ │ │ ├── OxfmtOnSaveFixAllActionInfo.kt
│ │ │ ├── OxfmtSettings.kt
│ │ │ └── OxfmtConfigurable.kt
│ │ ├── listeners
│ │ │ ├── OxfmtEditorWatcher.kt
│ │ │ └── OxfmtConfigWatcher.kt
│ │ ├── OxfmtSchemaProviderFactory.kt
│ │ ├── actions
│ │ │ ├── OxfmtFixAllOnSaveAction.kt
│ │ │ └── OxfmtFixAllAction.kt
│ │ ├── services
│ │ │ └── OxfmtServerService.kt
│ │ └── OxfmtPackage.kt
│ │ ├── settings
│ │ └── OxcOnSaveInfoProvider.kt
│ │ ├── OxcIconProvider.kt
│ │ ├── extensions
│ │ └── FileExt.kt
│ │ ├── NodeProcessCommandBuilder.kt
│ │ ├── GeneralProcessCommandBuilder.kt
│ │ └── OxcTargetRun.kt
│ └── resources
│ ├── messages
│ ├── OxfmtBundle.properties
│ └── OxlintBundle.properties
│ ├── icons
│ └── oxcRound.svg
│ └── META-INF
│ ├── pluginIcon.svg
│ └── plugin.xml
├── settings.gradle.kts
├── .github
├── SECURITY.md
├── renovate.json
├── CONTRIBUTING.md
└── workflows
│ ├── run-ui-tests.yml
│ └── release.yml
├── qodana.yml
├── .idea
└── gradle.xml
├── MAINTENANCE.md
├── .run
├── Run Tests.run.xml
├── Run Plugin.run.xml
└── Run Verifications.run.xml
├── LICENSE
├── README.md
├── gradle.properties
├── gradlew.bat
└── CHANGELOG.md
/.node-version:
--------------------------------------------------------------------------------
1 | 22.14.0
2 |
--------------------------------------------------------------------------------
/plugin-verifier-ignored-problems.txt:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/sandbox/oxfmt/nested-configs/.oxfmtrc.json:
--------------------------------------------------------------------------------
1 | {
2 | }
3 |
--------------------------------------------------------------------------------
/sandbox/oxfmt/nested-configs/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | .idea/
3 |
--------------------------------------------------------------------------------
/sandbox/oxlint/nested-configs/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | .idea/
3 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .gradle
2 | .idea
3 | .intellijPlatform
4 | .qodana
5 | build
6 | node_modules
7 |
--------------------------------------------------------------------------------
/sandbox/oxlint/custom-config/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | .idea/
3 | !.idea/OxcSettings.xml
4 |
--------------------------------------------------------------------------------
/sandbox/oxlint/custom-config/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "oxc.configPath": "./custom-oxlint.jsonc"
3 | }
4 |
--------------------------------------------------------------------------------
/sandbox/oxlint/nested-configs/package-b/index.js:
--------------------------------------------------------------------------------
1 | console.log('Happy developing ✨')
2 |
3 |
4 | new Array([])
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oxc-project/oxc-intellij-plugin/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/sandbox/oxlint/custom-config/index.js:
--------------------------------------------------------------------------------
1 | console.log('Happy developing ✨')
2 |
3 | debugger
4 | new Array([])
5 |
6 | if (foo) foo++;
7 |
--------------------------------------------------------------------------------
/sandbox/oxlint/nested-configs/index.js:
--------------------------------------------------------------------------------
1 | console.log('Happy developing ✨')
2 |
3 | debugger
4 | new Array([])
5 |
6 | if (foo) foo++;
7 |
--------------------------------------------------------------------------------
/sandbox/oxfmt/nested-configs/index.js:
--------------------------------------------------------------------------------
1 | console.log("Happy developing ✨");
2 |
3 | debugger;
4 | new Array([]);
5 |
6 | if (foo) {foo++;}
7 |
--------------------------------------------------------------------------------
/src/test/testData/oxlint/highlighting/nested-config/subdirectory/index.js:
--------------------------------------------------------------------------------
1 | console.log('Happy developing ✨')
2 |
3 |
4 | new Array([])
5 |
--------------------------------------------------------------------------------
/sandbox/oxlint/nested-configs/package-c/index.js:
--------------------------------------------------------------------------------
1 | console.log('Happy developing ✨')
2 |
3 | debugger
4 | new Array([])
5 |
6 | if (foo) foo++;
7 |
--------------------------------------------------------------------------------
/src/test/testData/oxlint/highlighting/no-config/index.js:
--------------------------------------------------------------------------------
1 | console.log('Happy developing ✨')
2 |
3 | debugger
4 | new Array([])
5 |
6 | if (foo) foo++;
7 |
--------------------------------------------------------------------------------
/src/test/testData/oxlint/highlighting/custom-config/index.js:
--------------------------------------------------------------------------------
1 | console.log('Happy developing ✨')
2 |
3 | debugger
4 | new Array([])
5 |
6 | if (foo) foo++;
7 |
--------------------------------------------------------------------------------
/src/test/testData/oxlint/highlighting/nested-config/index.js:
--------------------------------------------------------------------------------
1 | console.log('Happy developing ✨')
2 |
3 | debugger
4 | new Array([])
5 |
6 | if (foo) foo++;
7 |
--------------------------------------------------------------------------------
/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0"
3 | }
4 |
5 | rootProject.name = "oxc-intellij-plugin"
6 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/github/oxc/project/oxcintellijplugin/Constants.kt:
--------------------------------------------------------------------------------
1 | package com.github.oxc.project.oxcintellijplugin
2 |
3 | const val NOTIFICATION_GROUP = "Oxc"
4 |
--------------------------------------------------------------------------------
/sandbox/oxlint/nested-configs/package-a/index.js:
--------------------------------------------------------------------------------
1 | console.log('Happy developing ✨')
2 | debugger;
3 |
4 | new Array([])
5 |
6 | debugger
7 |
8 | (x < y || x === y)
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/src/test/testData/oxlint/highlighting/custom-config/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "oxc-lint",
3 | "version": "1.0.0",
4 | "private": true,
5 | "devDependencies": {
6 | "oxlint": "1.34.0"
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/src/test/testData/oxlint/highlighting/nested-config/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "oxc-lint",
3 | "version": "1.0.0",
4 | "private": true,
5 | "devDependencies": {
6 | "oxlint": "1.34.0"
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/src/test/testData/oxlint/highlighting/no-config/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "oxc-lint",
3 | "version": "1.0.0",
4 | "private": true,
5 | "devDependencies": {
6 | "oxlint": "1.34.0"
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/sandbox/oxlint/nested-configs/package-a/test.jsx:
--------------------------------------------------------------------------------
1 | var React = require('react');
2 |
3 | var Component = React.forwardRef((props) => (
4 |
5 | ));
6 |
7 | (x < y || x === y)
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/github/oxc/project/oxcintellijplugin/ConfigurationMode.kt:
--------------------------------------------------------------------------------
1 | package com.github.oxc.project.oxcintellijplugin
2 |
3 | enum class ConfigurationMode {
4 | DISABLED,
5 | AUTOMATIC,
6 | MANUAL
7 | }
8 |
--------------------------------------------------------------------------------
/.github/SECURITY.md:
--------------------------------------------------------------------------------
1 | # Security Policy
2 |
3 | ## Supported Versions
4 |
5 | ## Reporting a Vulnerability
6 |
7 | If you find any potential vulnerability, join our [discord channel](https://discord.gg/9uXCAwqQZW) and contact Boshen.
8 |
--------------------------------------------------------------------------------
/sandbox/oxlint/custom-config/custom-oxlint.jsonc:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": [],
3 | "categories": {},
4 | "rules": {
5 | "curly": "deny"
6 | },
7 | "env": {
8 | "builtin": true
9 | },
10 | "globals": {},
11 | "ignorePatterns": []
12 | }
13 |
--------------------------------------------------------------------------------
/qodana.yml:
--------------------------------------------------------------------------------
1 | # Qodana configuration:
2 | # https://www.jetbrains.com/help/qodana/qodana-yaml.html
3 |
4 | version: 1.0
5 | linter: jetbrains/qodana-jvm-community:latest
6 | projectJDK: "21"
7 | profile:
8 | name: qodana.recommended
9 | exclude:
10 | - name: All
11 | paths:
12 | - .qodana
13 |
--------------------------------------------------------------------------------
/sandbox/oxlint/custom-config/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "oxc-lint",
3 | "version": "1.0.0",
4 | "description": "",
5 | "scripts": {
6 | "test": "echo \"Error: no test specified\" && exit 1"
7 | },
8 | "private": true,
9 | "devDependencies": {
10 | "oxlint": "1.34.0"
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip
4 | networkTimeout=10000
5 | validateDistributionUrl=true
6 | zipStoreBase=GRADLE_USER_HOME
7 | zipStorePath=wrapper/dists
8 |
--------------------------------------------------------------------------------
/sandbox/oxlint/nested-configs/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "oxc-lint",
3 | "version": "1.0.0",
4 | "description": "",
5 | "scripts": {
6 | "test": "echo \"Error: no test specified\" && exit 1"
7 | },
8 | "private": true,
9 | "devDependencies": {
10 | "oxlint": "1.34.0"
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/github/oxc/project/oxcintellijplugin/OxcIcons.kt:
--------------------------------------------------------------------------------
1 | package com.github.oxc.project.oxcintellijplugin
2 |
3 | import com.intellij.openapi.util.IconLoader
4 |
5 | object OxcIcons {
6 |
7 | @JvmField
8 | val OxcRound = IconLoader.getIcon("/icons/oxcRound.svg", javaClass)
9 |
10 | }
11 |
--------------------------------------------------------------------------------
/sandbox/oxfmt/nested-configs/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "oxfmt-nested-configs",
3 | "version": "1.0.0",
4 | "description": "",
5 | "scripts": {
6 | "test": "echo \"Error: no test specified\" && exit 1"
7 | },
8 | "private": true,
9 | "devDependencies": {
10 | "oxfmt": "0.19.0"
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/test/testData/oxlint/highlighting/custom-config/custom-oxlint.jsonc:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "./node_modules/oxlint/configuration_schema.json",
3 | "plugins": [],
4 | "categories": {},
5 | "rules": {
6 | "curly": "deny"
7 | },
8 | "env": {
9 | "builtin": true
10 | },
11 | "globals": {},
12 | "ignorePatterns": []
13 | }
14 |
--------------------------------------------------------------------------------
/.github/renovate.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json",
3 | "extends": ["github>Boshen/renovate", "helpers:pinGitHubActionDigestsToSemver"],
4 | "packageRules": [
5 | {
6 | "groupName": "java",
7 | "matchManagers": ["gradle", "gradle-wrapper"],
8 | "rangeStrategy": "auto"
9 | }
10 | ]
11 | }
12 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/github/oxc/project/oxcintellijplugin/oxlint/OxlintRunTrigger.kt:
--------------------------------------------------------------------------------
1 | package com.github.oxc.project.oxcintellijplugin.oxlint
2 |
3 | enum class OxlintRunTrigger {
4 | ON_SAVE,
5 | ON_TYPE;
6 |
7 | fun toLspValue(): String {
8 | return when (this) {
9 | ON_SAVE -> "onSave"
10 | ON_TYPE -> "onType"
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/test/testData/oxlint/highlighting/custom-config/index.expected.js:
--------------------------------------------------------------------------------
1 | console.log('Happy developing ✨')
2 |
3 | debugger
5 | new Array([])
6 |
7 | if (foo) foo++;
9 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/github/oxc/project/oxcintellijplugin/oxlint/OxlintUnusedDisableDirectivesSeverity.kt:
--------------------------------------------------------------------------------
1 | package com.github.oxc.project.oxcintellijplugin.oxlint
2 |
3 | enum class OxlintUnusedDisableDirectivesSeverity {
4 | ALLOW,
5 | WARN,
6 | DENY;
7 |
8 | fun toLspValue(): String {
9 | return when (this) {
10 | ALLOW -> "allow"
11 | WARN -> "warn"
12 | DENY -> "deny"
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/test/testData/oxlint/highlighting/nested-config/subdirectory/index.expected.js:
--------------------------------------------------------------------------------
1 | console.log('Happy developing ✨')
2 |
3 |
4 | new Array([])
6 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/github/oxc/project/oxcintellijplugin/ProcessCommandParameter.kt:
--------------------------------------------------------------------------------
1 | package com.github.oxc.project.oxcintellijplugin
2 |
3 | import java.nio.file.Path
4 |
5 | sealed interface ProcessCommandParameter {
6 | class Value(val value: String) : ProcessCommandParameter {
7 | override fun toString() = value
8 | }
9 |
10 | class FilePath(val path: Path) : ProcessCommandParameter {
11 | override fun toString() = path.toString()
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/sandbox/oxlint/custom-config/.idea/OxcSettings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/src/test/testData/oxlint/highlighting/no-config/index.expected.js:
--------------------------------------------------------------------------------
1 | console.log('Happy developing ✨')
2 |
3 | debugger
5 | new Array([])
7 |
8 | if (foo) foo++;
9 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/github/oxc/project/oxcintellijplugin/ProcessCommandBuilder.kt:
--------------------------------------------------------------------------------
1 | package com.github.oxc.project.oxcintellijplugin
2 |
3 | import com.intellij.openapi.vfs.VirtualFile
4 | import java.nio.charset.Charset
5 |
6 | interface ProcessCommandBuilder {
7 | fun setWorkingDirectory(path: String?): ProcessCommandBuilder
8 | fun setInputFile(file: VirtualFile?): ProcessCommandBuilder
9 | fun addParameters(params: List): ProcessCommandBuilder
10 | fun setExecutable(executable: String): ProcessCommandBuilder
11 | fun setCharset(charset: Charset): ProcessCommandBuilder
12 | fun build(): OxcTargetRun
13 | }
14 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/github/oxc/project/oxcintellijplugin/oxlint/OxlintFixKind.kt:
--------------------------------------------------------------------------------
1 | package com.github.oxc.project.oxcintellijplugin.oxlint
2 |
3 | enum class OxlintFixKind {
4 | SAFE_FIX,
5 | SAFE_FIX_OR_SUGGESTION,
6 | DANGEROUS_FIX,
7 | DANGEROUS_FIX_OR_SUGGESTION,
8 | NONE,
9 | ALL;
10 |
11 | fun toLspValue(): String {
12 | return when (this) {
13 | SAFE_FIX -> "safe_fix"
14 | SAFE_FIX_OR_SUGGESTION -> "safe_fix_or_suggestion"
15 | DANGEROUS_FIX -> "dangerous_fix"
16 | DANGEROUS_FIX_OR_SUGGESTION -> "dangerous_fix_or_suggestion"
17 | NONE -> "none"
18 | ALL -> "all"
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/gradle/libs.versions.toml:
--------------------------------------------------------------------------------
1 | [versions]
2 | # libraries
3 | junit = "4.13.2"
4 |
5 | # plugins
6 | changelog = "2.5.0"
7 | intelliJPlatform = "2.10.5"
8 | kotlin = "2.2.21"
9 | kover = "0.9.4"
10 | qodana = "2025.2.4"
11 |
12 | [libraries]
13 | junit = { group = "junit", name = "junit", version.ref = "junit" }
14 |
15 | [plugins]
16 | changelog = { id = "org.jetbrains.changelog", version.ref = "changelog" }
17 | intelliJPlatform = { id = "org.jetbrains.intellij.platform", version.ref = "intelliJPlatform" }
18 | kotlin = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
19 | kover = { id = "org.jetbrains.kotlinx.kover", version.ref = "kover" }
20 | qodana = { id = "org.jetbrains.qodana", version.ref = "qodana" }
21 |
--------------------------------------------------------------------------------
/src/test/testData/oxlint/highlighting/nested-config/index.expected.js:
--------------------------------------------------------------------------------
1 | console.log('Happy developing ✨')
2 |
3 | debugger
5 | new Array([])
7 |
8 | if (foo) foo++;
10 |
--------------------------------------------------------------------------------
/MAINTENANCE.md:
--------------------------------------------------------------------------------
1 | ## Release
2 |
3 | 1. Ensure that the UNRELEASED section of the [CHANGELOG.md](./CHANGELOG.md) is up to date with all relevant changes.
4 | See https://github.com/JetBrains/gradle-changelog-plugin for the changelog format.
5 | 2. Update the `pluginVersion` within [gradle.properties](./gradle.properties).
6 | 3. Every build creates a draft release that can be found
7 | here https://github.com/oxc-project/oxc-intellij-plugin/releases. These are all release candidates.
8 | 4. When ready to release, go to the current draft release and publish it. This will
9 | run [release.yml](./.github/workflows/release.yml) which will automatically create a PR to move the changes from the
10 | UNRELEASED section of the changelog to the appropriate version.
11 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/github/oxc/project/oxcintellijplugin/oxfmt/OxfmtBundle.kt:
--------------------------------------------------------------------------------
1 | package com.github.oxc.project.oxcintellijplugin.oxfmt
2 |
3 | import com.intellij.DynamicBundle
4 | import org.jetbrains.annotations.NonNls
5 | import org.jetbrains.annotations.PropertyKey
6 |
7 | @NonNls
8 | private const val OXFMT_BUNDLE = "messages.OxfmtBundle"
9 |
10 | object OxfmtBundle : DynamicBundle(OXFMT_BUNDLE) {
11 |
12 | @JvmStatic
13 | fun message(@PropertyKey(resourceBundle = OXFMT_BUNDLE) key: String,
14 | vararg params: Any) =
15 | getMessage(key, *params)
16 |
17 | @Suppress("unused")
18 | @JvmStatic
19 | fun messagePointer(@PropertyKey(resourceBundle = OXFMT_BUNDLE) key: String,
20 | vararg params: Any) =
21 | getLazyMessage(key, *params)
22 | }
23 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/github/oxc/project/oxcintellijplugin/oxlint/OxlintBundle.kt:
--------------------------------------------------------------------------------
1 | package com.github.oxc.project.oxcintellijplugin.oxlint
2 |
3 | import com.intellij.DynamicBundle
4 | import org.jetbrains.annotations.NonNls
5 | import org.jetbrains.annotations.PropertyKey
6 |
7 | @NonNls
8 | private const val OXLINT_BUNDLE = "messages.OxlintBundle"
9 |
10 | object OxlintBundle : DynamicBundle(OXLINT_BUNDLE) {
11 |
12 | @JvmStatic
13 | fun message(@PropertyKey(resourceBundle = OXLINT_BUNDLE) key: String,
14 | vararg params: Any) =
15 | getMessage(key, *params)
16 |
17 | @Suppress("unused")
18 | @JvmStatic
19 | fun messagePointer(@PropertyKey(resourceBundle = OXLINT_BUNDLE) key: String,
20 | vararg params: Any) =
21 | getLazyMessage(key, *params)
22 | }
23 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/github/oxc/project/oxcintellijplugin/oxfmt/lsp/OxfmtLspFormattingSupport.kt:
--------------------------------------------------------------------------------
1 | package com.github.oxc.project.oxcintellijplugin.oxfmt.lsp
2 |
3 | import com.github.oxc.project.oxcintellijplugin.oxfmt.settings.OxfmtSettings
4 | import com.intellij.openapi.diagnostic.thisLogger
5 | import com.intellij.openapi.project.Project
6 | import com.intellij.openapi.vfs.VirtualFile
7 | import com.intellij.platform.lsp.api.customization.LspFormattingSupport
8 |
9 | class OxfmtLspFormattingSupport(private val project: Project) : LspFormattingSupport() {
10 |
11 | override fun shouldFormatThisFileExclusivelyByServer(file: VirtualFile,
12 | ideCanFormatThisFileItself: Boolean,
13 | serverExplicitlyWantsToFormatThisFile: Boolean): Boolean {
14 | val shouldFormatFile = OxfmtSettings.getInstance(project).fileSupported(file)
15 | thisLogger().debug("Should format ${file.path} = $shouldFormatFile")
16 |
17 | return shouldFormatFile
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/github/oxc/project/oxcintellijplugin/oxfmt/settings/OxfmtSettingsState.kt:
--------------------------------------------------------------------------------
1 | package com.github.oxc.project.oxcintellijplugin.oxfmt.settings
2 |
3 | import com.github.oxc.project.oxcintellijplugin.ConfigurationMode
4 | import com.intellij.openapi.components.BaseState
5 | import com.intellij.util.xml.Attribute
6 |
7 | class OxfmtSettingsState : BaseState() {
8 |
9 | @get:Attribute("binaryPath")
10 | var binaryPath by string()
11 |
12 | @get:Attribute("configurationMode")
13 | var configurationMode by enum(ConfigurationMode.AUTOMATIC)
14 |
15 | @get:Attribute("configPath")
16 | var configPath by string()
17 |
18 | @get:Attribute("fixAllOnSave")
19 | var fixAllOnSave by property(false)
20 |
21 | @get:Attribute("supportedExtensions")
22 | var supportedExtensions by list()
23 |
24 | companion object {
25 |
26 | val DEFAULT_EXTENSION_LIST = listOf(
27 | ".js", ".jsx", ".cjs", ".mjs",
28 | ".ts", ".tsx", ".cts", ".mts",
29 | )
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/sandbox/oxlint/nested-configs/package-b/.oxlintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "../node_modules/oxlint/configuration_schema.json",
3 | "plugins": [
4 | "react",
5 | "unicorn",
6 | "typescript",
7 | "oxc"
8 | ],
9 | "categories": {},
10 | "rules": {
11 | "unicorn/no-new-array": "error"
12 | },
13 | "settings": {
14 | "jsx-a11y": {
15 | "polymorphicPropName": null,
16 | "components": {}
17 | },
18 | "next": {
19 | "rootDir": []
20 | },
21 | "react": {
22 | "formComponents": [],
23 | "linkComponents": []
24 | },
25 | "jsdoc": {
26 | "ignorePrivate": false,
27 | "ignoreInternal": false,
28 | "ignoreReplacesDocs": true,
29 | "overrideReplacesDocs": true,
30 | "augmentsExtendsReplacesDocs": false,
31 | "implementsReplacesDocs": false,
32 | "exemptDestructuredRootsFromChecks": false,
33 | "tagNamePreference": {}
34 | }
35 | },
36 | "env": {
37 | "builtin": true
38 | },
39 | "globals": {},
40 | "ignorePatterns": []
41 | }
42 |
--------------------------------------------------------------------------------
/src/test/testData/oxlint/highlighting/nested-config/subdirectory/.oxlintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "../node_modules/oxlint/configuration_schema.json",
3 | "plugins": [
4 | "react",
5 | "unicorn",
6 | "typescript",
7 | "oxc"
8 | ],
9 | "categories": {},
10 | "rules": {
11 | "unicorn/no-new-array": "error"
12 | },
13 | "settings": {
14 | "jsx-a11y": {
15 | "polymorphicPropName": null,
16 | "components": {}
17 | },
18 | "next": {
19 | "rootDir": []
20 | },
21 | "react": {
22 | "formComponents": [],
23 | "linkComponents": []
24 | },
25 | "jsdoc": {
26 | "ignorePrivate": false,
27 | "ignoreInternal": false,
28 | "ignoreReplacesDocs": true,
29 | "overrideReplacesDocs": true,
30 | "augmentsExtendsReplacesDocs": false,
31 | "implementsReplacesDocs": false,
32 | "exemptDestructuredRootsFromChecks": false,
33 | "tagNamePreference": {}
34 | }
35 | },
36 | "env": {
37 | "builtin": true
38 | },
39 | "globals": {},
40 | "ignorePatterns": []
41 | }
42 |
--------------------------------------------------------------------------------
/.run/Run Tests.run.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | true
20 | true
21 | false
22 | true
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/github/oxc/project/oxcintellijplugin/oxfmt/listeners/OxfmtEditorWatcher.kt:
--------------------------------------------------------------------------------
1 | package com.github.oxc.project.oxcintellijplugin.oxfmt.listeners
2 |
3 | import com.github.oxc.project.oxcintellijplugin.oxfmt.services.OxfmtServerService
4 | import com.github.oxc.project.oxcintellijplugin.oxfmt.settings.OxfmtSettings
5 | import com.intellij.openapi.fileEditor.FileEditorManager
6 | import com.intellij.openapi.fileEditor.FileEditorManagerListener
7 | import com.intellij.openapi.vfs.VirtualFile
8 |
9 | class OxfmtEditorWatcher : FileEditorManagerListener {
10 |
11 | override fun fileClosed(source: FileEditorManager, file: VirtualFile) {
12 | val project = source.project
13 | if (source.allEditors.isEmpty()) {
14 | OxfmtServerService.getInstance(project).stopServer()
15 | return
16 | }
17 |
18 | val stillHasSupportedFileOpen = source.allEditors.any {
19 | OxfmtSettings.getInstance(project).fileSupported(it.file)
20 | }
21 | if (!stillHasSupportedFileOpen) {
22 | OxfmtServerService.getInstance(project).stopServer()
23 | }
24 | }
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/.run/Run Plugin.run.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | true
20 | true
21 | false
22 | false
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/.github/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | ### Running Tests
2 |
3 | Tests can be executed with `./gradlew check`. Prior to running tests, you'll need to execute `npm install` within each
4 | of the test directories found at `src/test/testData`.
5 |
6 | If you fail to execute `npm install` within a test directory, you'll typically receive an exception like the below.
7 |
8 | ```
9 | Caused by: java.io.FileNotFoundException: /private/var/folders/y9/7_0wzf515vsdjc62stxjngnc0000gn/T/unitTest_rootFileHighlighting_2yF2NjR6MfHBbuoM6d9V6JBZdFT/unitTest13365413598591665525/node_modules/oxlint/bin/oxc_language_server (No such file or directory)
10 | at java.base/java.io.FileInputStream.open0(Native Method)
11 | at java.base/java.io.FileInputStream.open(FileInputStream.java:213)
12 | at java.base/java.io.FileInputStream.(FileInputStream.java:152)
13 | at com.github.oxc.project.oxcintellijplugin.OxcTargetRunBuilder.getBuilder(OxcTargetRun.kt:99)
14 | at com.github.oxc.project.oxcintellijplugin.lsp.OxcLspServerDescriptor.(OxcLspServerDescriptor.kt:24)
15 | at com.github.oxc.project.oxcintellijplugin.lsp.OxcLspServerSupportProvider.fileOpened(OxcLspServerSupportProvider.kt:41)
16 | ```
17 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024-present VoidZero Inc. & Contributors
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/github/oxc/project/oxcintellijplugin/oxlint/listeners/OxlintEditorWatcher.kt:
--------------------------------------------------------------------------------
1 | package com.github.oxc.project.oxcintellijplugin.oxlint.listeners
2 |
3 | import com.github.oxc.project.oxcintellijplugin.oxlint.services.OxlintServerService
4 | import com.github.oxc.project.oxcintellijplugin.oxlint.settings.OxlintSettings
5 | import com.intellij.openapi.fileEditor.FileEditorManager
6 | import com.intellij.openapi.fileEditor.FileEditorManagerListener
7 | import com.intellij.openapi.vfs.VirtualFile
8 |
9 | class OxlintEditorWatcher : FileEditorManagerListener {
10 |
11 | override fun fileClosed(source: FileEditorManager, file: VirtualFile) {
12 | val project = source.project
13 | if (source.allEditors.isEmpty()) {
14 | OxlintServerService.getInstance(project).stopServer()
15 | return
16 | }
17 |
18 | val stillHasSupportedFileOpen = source.allEditors.any {
19 | OxlintSettings.getInstance(project).fileSupported(it.file)
20 | }
21 | if (!stillHasSupportedFileOpen) {
22 | OxlintServerService.getInstance(project).stopServer()
23 | }
24 | }
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/sandbox/oxlint/nested-configs/package-a/.oxlintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "../node_modules/oxlint/configuration_schema.json",
3 | "plugins": [
4 | "react",
5 | "unicorn",
6 | "typescript",
7 | "oxc"
8 | ],
9 | "categories": {},
10 | "rules": {
11 | "unicorn/no-new-array": "warn",
12 | "eslint/no-debugger": "error",
13 | "oxc/double-comparisons": "error",
14 | "react/forward-ref-uses-ref": "error"
15 | },
16 | "settings": {
17 | "jsx-a11y": {
18 | "polymorphicPropName": null,
19 | "components": {}
20 | },
21 | "next": {
22 | "rootDir": []
23 | },
24 | "react": {
25 | "formComponents": [],
26 | "linkComponents": []
27 | },
28 | "jsdoc": {
29 | "ignorePrivate": false,
30 | "ignoreInternal": false,
31 | "ignoreReplacesDocs": true,
32 | "overrideReplacesDocs": true,
33 | "augmentsExtendsReplacesDocs": false,
34 | "implementsReplacesDocs": false,
35 | "exemptDestructuredRootsFromChecks": false,
36 | "tagNamePreference": {}
37 | }
38 | },
39 | "env": {
40 | "builtin": true
41 | },
42 | "globals": {},
43 | "ignorePatterns": []
44 | }
45 |
--------------------------------------------------------------------------------
/.run/Run Verifications.run.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | true
21 | true
22 | false
23 | false
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/github/oxc/project/oxcintellijplugin/settings/OxcOnSaveInfoProvider.kt:
--------------------------------------------------------------------------------
1 | package com.github.oxc.project.oxcintellijplugin.settings
2 |
3 | import com.github.oxc.project.oxcintellijplugin.oxfmt.OxfmtBundle
4 | import com.github.oxc.project.oxcintellijplugin.oxfmt.settings.OxfmtOnSaveFixAllActionInfo
5 | import com.github.oxc.project.oxcintellijplugin.oxlint.OxlintBundle
6 | import com.github.oxc.project.oxcintellijplugin.oxlint.settings.OxlintOnSaveFixAllActionInfo
7 | import com.intellij.ide.actionsOnSave.ActionOnSaveContext
8 | import com.intellij.ide.actionsOnSave.ActionOnSaveInfo
9 | import com.intellij.ide.actionsOnSave.ActionOnSaveInfoProvider
10 |
11 | class OxcOnSaveInfoProvider : ActionOnSaveInfoProvider() {
12 |
13 | override fun getActionOnSaveInfos(context: ActionOnSaveContext): List =
14 | listOf(OxlintOnSaveFixAllActionInfo(context), OxfmtOnSaveFixAllActionInfo(context))
15 |
16 | override fun getSearchableOptions(): Collection {
17 | return listOf(
18 | OxlintBundle.message("oxlint.fix.all.on.save.checkbox.on.actions.on.save.page"),
19 | OxfmtBundle.message("oxfmt.fix.all.on.save.checkbox.on.actions.on.save.page"))
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/github/oxc/project/oxcintellijplugin/oxlint/lsp/OxlintLspDiagnosticsSupport.kt:
--------------------------------------------------------------------------------
1 | package com.github.oxc.project.oxcintellijplugin.oxlint.lsp
2 |
3 | import com.github.oxc.project.oxcintellijplugin.oxlint.OxlintBundle
4 | import com.intellij.openapi.diagnostic.thisLogger
5 | import com.intellij.platform.lsp.api.customization.LspDiagnosticsSupport
6 | import org.eclipse.lsp4j.Diagnostic
7 |
8 | class OxlintLspDiagnosticsSupport : LspDiagnosticsSupport() {
9 |
10 | override fun getMessage(diagnostic: Diagnostic): String {
11 | thisLogger().debug("Creating message for diagnostic: $diagnostic")
12 | return "${diagnostic.source}: ${diagnostic.message} ${
13 | diagnostic.code?.get() ?: OxlintBundle.message("oxlint.diagnostic.unknown.code")
14 | }"
15 | }
16 |
17 | override fun getTooltip(diagnostic: Diagnostic): String {
18 | thisLogger().debug("Creating tooltip for diagnostic: $diagnostic")
19 | var rule = diagnostic.code?.get() ?: OxlintBundle.message("oxlint.diagnostic.unknown.code")
20 | if (diagnostic.codeDescription?.href != null) {
21 | rule = "${rule}"
22 | }
23 |
24 | return "${diagnostic.source}: ${diagnostic.message} $rule"
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/test/kotlin/com/github/oxc/project/oxcintellijplugin/oxlint/NoConfigHighlightingTest.kt:
--------------------------------------------------------------------------------
1 | package com.github.oxc.project.oxcintellijplugin.oxlint
2 |
3 | import com.github.oxc.project.oxcintellijplugin.extensions.configureByFileAndCheckLanguageServerHighlighting
4 | import com.github.oxc.project.oxcintellijplugin.oxlint.lsp.OxlintLspServerDescriptor
5 | import com.intellij.testFramework.TestDataPath
6 | import com.intellij.testFramework.builders.ModuleFixtureBuilder
7 | import com.intellij.testFramework.fixtures.CodeInsightFixtureTestCase
8 | import com.intellij.testFramework.fixtures.ModuleFixture
9 | import com.intellij.testFramework.fixtures.impl.CodeInsightTestFixtureImpl
10 |
11 | @TestDataPath("\$CONTENT_ROOT/testData/oxlint/highlighting")
12 | class NoConfigHighlightingTest :
13 | CodeInsightFixtureTestCase>() {
14 |
15 | override fun setUp() {
16 | super.setUp()
17 | (myFixture as CodeInsightTestFixtureImpl).canChangeDocumentDuringHighlighting(true)
18 | myFixture.testDataPath = "src/test/testData/oxlint/highlighting"
19 | }
20 |
21 | fun testRootFileHighlighting() {
22 | myFixture.copyDirectoryToProject("no-config", "")
23 |
24 | myFixture.configureByFileAndCheckLanguageServerHighlighting(OxlintLspServerDescriptor::class.java, "index.expected.js")
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/github/oxc/project/oxcintellijplugin/OxcIconProvider.kt:
--------------------------------------------------------------------------------
1 | package com.github.oxc.project.oxcintellijplugin
2 |
3 | import com.github.oxc.project.oxcintellijplugin.extensions.isOxfmtConfigFile
4 | import com.github.oxc.project.oxcintellijplugin.extensions.isOxlintConfigFile
5 | import com.github.oxc.project.oxcintellijplugin.oxlint.settings.OxlintSettings
6 | import com.intellij.ide.IconProvider
7 | import com.intellij.openapi.project.DumbAware
8 | import com.intellij.psi.PsiElement
9 | import com.intellij.psi.PsiFile
10 | import javax.swing.Icon
11 |
12 | class OxcIconProvider : IconProvider(), DumbAware {
13 |
14 | override fun getIcon(element: PsiElement,
15 | flags: Int): Icon? {
16 | if (element !is PsiFile) {
17 | return null
18 | }
19 | val file = element.viewProvider.virtualFile
20 | if (!file.isValid || file.isDirectory) {
21 | return null
22 | }
23 | val settings = OxlintSettings.getInstance(element.project)
24 | if (settings.state.configPath == file.path) {
25 | return OxcIcons.OxcRound
26 | }
27 | if (file.isOxlintConfigFile()) {
28 | return OxcIcons.OxcRound
29 | }
30 | if (file.isOxfmtConfigFile()) {
31 | return OxcIcons.OxcRound
32 | }
33 |
34 | return null
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/github/oxc/project/oxcintellijplugin/oxfmt/listeners/OxfmtConfigWatcher.kt:
--------------------------------------------------------------------------------
1 | package com.github.oxc.project.oxcintellijplugin.oxfmt.listeners
2 |
3 | import com.github.oxc.project.oxcintellijplugin.extensions.isOxfmtConfigFile
4 | import com.github.oxc.project.oxcintellijplugin.oxfmt.services.OxfmtServerService
5 | import com.intellij.openapi.application.ApplicationManager
6 | import com.intellij.openapi.components.service
7 | import com.intellij.openapi.project.ProjectManager
8 | import com.intellij.openapi.vfs.newvfs.BulkFileListener
9 | import com.intellij.openapi.vfs.newvfs.events.VFileEvent
10 |
11 | class OxfmtConfigWatcher : BulkFileListener {
12 |
13 | override fun after(events: List) {
14 | val configChanged = events.any { event ->
15 | return@any event.file?.isOxfmtConfigFile() == true
16 | }
17 |
18 | if (configChanged) {
19 | val projectManager = ApplicationManager.getApplication().service()
20 | val openProjects = projectManager.openProjects
21 | openProjects.forEach { project ->
22 | ApplicationManager.getApplication().invokeLater {
23 | if (!project.isDisposed) {
24 | OxfmtServerService.getInstance(project).restartServer()
25 | }
26 | }
27 | }
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/github/oxc/project/oxcintellijplugin/oxlint/listeners/OxlintConfigWatcher.kt:
--------------------------------------------------------------------------------
1 | package com.github.oxc.project.oxcintellijplugin.oxlint.listeners
2 |
3 | import com.github.oxc.project.oxcintellijplugin.extensions.isOxlintConfigFile
4 | import com.github.oxc.project.oxcintellijplugin.oxlint.services.OxlintServerService
5 | import com.intellij.openapi.application.ApplicationManager
6 | import com.intellij.openapi.components.service
7 | import com.intellij.openapi.project.ProjectManager
8 | import com.intellij.openapi.vfs.newvfs.BulkFileListener
9 | import com.intellij.openapi.vfs.newvfs.events.VFileEvent
10 |
11 | class OxlintConfigWatcher : BulkFileListener {
12 | override fun after(events: List) {
13 | val configChanged = events.any { event ->
14 | return@any event.file?.isOxlintConfigFile() == true
15 | }
16 |
17 | if (configChanged) {
18 | val projectManager = ApplicationManager.getApplication().service()
19 | val openProjects = projectManager.openProjects
20 | openProjects.forEach { project ->
21 | ApplicationManager.getApplication().invokeLater {
22 | if (!project.isDisposed) {
23 | OxlintServerService.getInstance(project).restartServer()
24 | }
25 | }
26 | }
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/main/resources/messages/OxfmtBundle.properties:
--------------------------------------------------------------------------------
1 | oxfmt.config.path.label=Path to Oxfmt Config:
2 | oxfmt.configure.extensions.link=Configure file extensions
3 | oxfmt.file.not.supported.description=The file "{0}" is not supported by Oxfmt.
4 | oxfmt.file.not.supported.title=Unsupported file
5 | oxfmt.fix.all.failure.description=An error occurred while applying 'Fix All': {0}
6 | oxfmt.fix.all.failure.label=Failed to Apply 'Fix All'
7 | oxfmt.fix.all.on.save.checkbox.on.actions.on.save.page=Run 'Fix All' with Oxfmt on Save
8 | oxfmt.fix.all.on.save.failure.description=An error occurred while running 'Fix All' on save: {0}
9 | oxfmt.fix.all.on.save.failure.label=Failed to apply 'Fix all' on save
10 | oxfmt.fix.all.success.description='Fix All' successfully applied.
11 | oxfmt.fix.all.success.label=Fixes applied successfully
12 | oxfmt.language.server.restarted=Oxfmt language server restarted.
13 | oxfmt.name=Oxfmt
14 | oxfmt.on.save.comment.disabled=Oxfmt integration is disabled.
15 | oxfmt.on.save.comment.fix.all=Oxfmt 'Fix All'
16 | oxfmt.run.fix.all=Running Oxfmt 'Fix All'
17 | oxfmt.run.fix.all.on.save.checkbox.on.actions.on.save.page=Run Oxfmt 'Fix All' on Save
18 | oxfmt.run.fix.all.on.save.label=Run 'Fix All' on &Save
19 | oxfmt.run.quickfix=Running Oxfmt Quickfix
20 | oxfmt.schema.name=Oxfmt
21 | oxfmt.settings.languageServerPath=Path to Oxfmt Language Server:
22 | oxfmt.supported.extensions.comment=Enter file extensions separated by commas (e.g., .js, .ts, .jsx). Each extension must start with '.' and contain only alphanumeric characters.
23 | oxfmt.supported.extensions.label=Supported File Extensions:
24 |
--------------------------------------------------------------------------------
/src/test/kotlin/com/github/oxc/project/oxcintellijplugin/oxlint/NestedConfigHighlightingTest.kt:
--------------------------------------------------------------------------------
1 | package com.github.oxc.project.oxcintellijplugin.oxlint
2 |
3 | import com.github.oxc.project.oxcintellijplugin.extensions.configureByFileAndCheckLanguageServerHighlighting
4 | import com.github.oxc.project.oxcintellijplugin.oxlint.lsp.OxlintLspServerDescriptor
5 | import com.intellij.testFramework.TestDataPath
6 | import com.intellij.testFramework.builders.ModuleFixtureBuilder
7 | import com.intellij.testFramework.fixtures.CodeInsightFixtureTestCase
8 | import com.intellij.testFramework.fixtures.ModuleFixture
9 | import com.intellij.testFramework.fixtures.impl.CodeInsightTestFixtureImpl
10 |
11 | @TestDataPath("\$CONTENT_ROOT/testData/oxlint/highlighting")
12 | class NestedConfigHighlightingTest :
13 | CodeInsightFixtureTestCase>() {
14 |
15 | override fun setUp() {
16 | super.setUp()
17 | (myFixture as CodeInsightTestFixtureImpl).canChangeDocumentDuringHighlighting(true)
18 | myFixture.testDataPath = "src/test/testData/oxlint/highlighting"
19 | }
20 |
21 | fun testRootFileHighlighting() {
22 | myFixture.copyDirectoryToProject("nested-config", "")
23 |
24 | myFixture.configureByFileAndCheckLanguageServerHighlighting(OxlintLspServerDescriptor::class.java, "index.expected.js")
25 | }
26 |
27 | fun testSubdirectoryFileHighlighting() {
28 | myFixture.copyDirectoryToProject("nested-config", "")
29 |
30 | myFixture.configureByFileAndCheckLanguageServerHighlighting(OxlintLspServerDescriptor::class.java, "subdirectory/index.expected.js")
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/github/oxc/project/oxcintellijplugin/oxfmt/OxfmtSchemaProviderFactory.kt:
--------------------------------------------------------------------------------
1 | package com.github.oxc.project.oxcintellijplugin.oxfmt
2 |
3 | import com.github.oxc.project.oxcintellijplugin.extensions.isOxfmtConfigFile
4 | import com.github.oxc.project.oxcintellijplugin.oxfmt.settings.OxfmtSettings
5 | import com.intellij.openapi.project.Project
6 | import com.intellij.openapi.vfs.VirtualFile
7 | import com.jetbrains.jsonSchema.extension.JsonSchemaFileProvider
8 | import com.jetbrains.jsonSchema.extension.JsonSchemaProviderFactory
9 | import com.jetbrains.jsonSchema.extension.SchemaType
10 | import com.jetbrains.jsonSchema.remote.JsonFileResolver
11 | import org.jetbrains.annotations.Nls
12 |
13 | class OxfmtSchemaProviderFactory : JsonSchemaProviderFactory {
14 |
15 | override fun getProviders(project: Project): List {
16 | return listOf(object : JsonSchemaFileProvider {
17 | override fun isAvailable(file: VirtualFile): Boolean {
18 | val settings = OxfmtSettings.getInstance(project)
19 | return settings.state.configPath == file.path || file.isOxfmtConfigFile()
20 | }
21 |
22 | override fun getName(): @Nls String {
23 | return OxfmtBundle.message("oxfmt.schema.name")
24 | }
25 |
26 | override fun getSchemaFile(): VirtualFile? {
27 | return JsonFileResolver.urlToFile(
28 | "https://raw.githubusercontent.com/oxc-project/oxc/main/npm/oxfmt/configuration_schema.json")
29 | }
30 |
31 | override fun getSchemaType(): SchemaType {
32 | return SchemaType.remoteSchema
33 | }
34 | })
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/github/oxc/project/oxcintellijplugin/oxlint/OxlintSchemaProviderFactory.kt:
--------------------------------------------------------------------------------
1 | package com.github.oxc.project.oxcintellijplugin.oxlint
2 |
3 | import com.github.oxc.project.oxcintellijplugin.extensions.isOxlintConfigFile
4 | import com.github.oxc.project.oxcintellijplugin.oxlint.settings.OxlintSettings
5 | import com.intellij.openapi.project.Project
6 | import com.intellij.openapi.vfs.VirtualFile
7 | import com.jetbrains.jsonSchema.extension.JsonSchemaFileProvider
8 | import com.jetbrains.jsonSchema.extension.JsonSchemaProviderFactory
9 | import com.jetbrains.jsonSchema.extension.SchemaType
10 | import com.jetbrains.jsonSchema.remote.JsonFileResolver
11 | import org.jetbrains.annotations.Nls
12 |
13 | class OxlintSchemaProviderFactory : JsonSchemaProviderFactory {
14 |
15 | override fun getProviders(project: Project): List {
16 | return listOf(object : JsonSchemaFileProvider {
17 | override fun isAvailable(file: VirtualFile): Boolean {
18 | val settings = OxlintSettings.getInstance(project)
19 | return settings.state.configPath == file.path || file.isOxlintConfigFile()
20 | }
21 |
22 | override fun getName(): @Nls String {
23 | return OxlintBundle.message("oxlint.schema.name")
24 | }
25 |
26 | override fun getSchemaFile(): VirtualFile? {
27 | return JsonFileResolver.urlToFile(
28 | "https://raw.githubusercontent.com/oxc-project/oxc/main/npm/oxlint/configuration_schema.json")
29 | }
30 |
31 | override fun getSchemaType(): SchemaType {
32 | return SchemaType.remoteSchema
33 | }
34 | })
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # oxc-intellij-plugin
2 |
3 | [](https://github.com/oxc-project/oxc-intellij-plugin/actions/workflows/build.yml?query=branch%3Amain)
4 | [](https://plugins.jetbrains.com/plugin/27061-oxc)
5 | [](https://plugins.jetbrains.com/plugin/27061-oxc)
6 |
7 |
8 |
9 | # Oxc
10 |
11 | The Oxidation Compiler is creating a suite of high-performance tools for JavaScript and TypeScript.
12 |
13 | ## Oxlint
14 |
15 | This is the linter for oxc. The currently supported features are listed below.
16 |
17 | - Highlighting for warnings or errors identified by Oxlint.
18 | - Quick fixes to fix a warning or error when possible.
19 | - JSON schema validation for `.oxlintrc.json` configuration files. (Note: Comments within the .oxlintrc.json
20 | file are supported, however they show as an error within the IDE due to jsonc not being supported by the IDE.)
21 | - Command to fix all auto-fixable content within the current text editor.
22 | - Automatically apply fixes on save.
23 |
24 |
25 |
26 | ## Installation
27 |
28 | - Using the IDE built-in plugin system:
29 |
30 | Settings/Preferences > Plugins > Marketplace > Search for "Oxc" >
31 | Install
32 |
33 | - Manually:
34 |
35 | Download the [latest release](https://github.com/oxc-project/oxc-intellij-plugin/releases/latest) and install it
36 | manually using
37 | Settings/Preferences > Plugins > ⚙️ > Install plugin from disk...
38 |
39 | ---
40 |
--------------------------------------------------------------------------------
/src/test/kotlin/com/github/oxc/project/oxcintellijplugin/oxlint/CustomConfigHighlightingTest.kt:
--------------------------------------------------------------------------------
1 | package com.github.oxc.project.oxcintellijplugin.oxlint
2 |
3 | import com.github.oxc.project.oxcintellijplugin.ConfigurationMode
4 | import com.github.oxc.project.oxcintellijplugin.extensions.configureByFileAndCheckLanguageServerHighlighting
5 | import com.github.oxc.project.oxcintellijplugin.oxlint.lsp.OxlintLspServerDescriptor
6 | import com.github.oxc.project.oxcintellijplugin.oxlint.settings.OxlintSettings
7 | import com.intellij.testFramework.TestDataPath
8 | import com.intellij.testFramework.builders.ModuleFixtureBuilder
9 | import com.intellij.testFramework.fixtures.CodeInsightFixtureTestCase
10 | import com.intellij.testFramework.fixtures.ModuleFixture
11 | import com.intellij.testFramework.fixtures.impl.CodeInsightTestFixtureImpl
12 |
13 | @TestDataPath("\$CONTENT_ROOT/testData/oxlint/highlighting")
14 | class CustomConfigHighlightingTest :
15 | CodeInsightFixtureTestCase>() {
16 |
17 | override fun setUp() {
18 | super.setUp()
19 | (myFixture as CodeInsightTestFixtureImpl).canChangeDocumentDuringHighlighting(true)
20 | myFixture.testDataPath = "src/test/testData/oxlint/highlighting"
21 |
22 | val oxlintSettings = OxlintSettings.getInstance(myFixture.project)
23 | oxlintSettings.configPath = "${myFixture.tempDirPath}/custom-oxlint.jsonc"
24 | oxlintSettings.configurationMode = ConfigurationMode.MANUAL
25 | }
26 |
27 | fun testRootFileHighlighting() {
28 | myFixture.copyDirectoryToProject("custom-config", "")
29 |
30 | myFixture.configureByFileAndCheckLanguageServerHighlighting(OxlintLspServerDescriptor::class.java, "index.expected.js")
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/github/oxc/project/oxcintellijplugin/extensions/FileExt.kt:
--------------------------------------------------------------------------------
1 | package com.github.oxc.project.oxcintellijplugin.extensions
2 |
3 | import com.github.oxc.project.oxcintellijplugin.oxfmt.OxfmtPackage
4 | import com.github.oxc.project.oxcintellijplugin.oxlint.OxlintPackage
5 | import com.intellij.openapi.vfs.VfsUtil
6 | import com.intellij.openapi.vfs.VirtualFile
7 |
8 | /**
9 | * Find the nearest file that satisfies the predicate.
10 | * If `root` is not null, stops finding at the specified directory.
11 | */
12 | private fun VirtualFile.findNearestFile(
13 | predicate: (file: VirtualFile) -> Boolean,
14 | root: VirtualFile? = null,
15 | ): VirtualFile? {
16 | var cur = this.parent
17 | while (cur != null && VfsUtil.isUnder(cur, mutableSetOf(root))) {
18 | val f = cur.children.find(predicate)
19 | if (f != null) {
20 | return f
21 | }
22 | cur = cur.parent
23 | }
24 | return null
25 | }
26 |
27 | fun VirtualFile.isOxlintConfigFile(): Boolean =
28 | OxlintPackage.configValidExtensions.map { "${OxlintPackage.CONFIG_NAME}.$it" }.contains(this.name)
29 |
30 | fun VirtualFile.isOxfmtConfigFile(): Boolean =
31 | OxfmtPackage.CONFIG_VALID_EXTENSIONS.map { "${OxfmtPackage.CONFIG_NAME}.$it" }.contains(this.name)
32 |
33 | fun VirtualFile.isPackageJsonFile(): Boolean = this.name == "package.json"
34 |
35 | fun VirtualFile.findNearestOxfmtConfig(root: VirtualFile? = null): VirtualFile? =
36 | this.findNearestFile({ f -> f.isOxfmtConfigFile() }, root)
37 |
38 | fun VirtualFile.findNearestOxlintConfig(root: VirtualFile? = null): VirtualFile? =
39 | this.findNearestFile({ f -> f.isOxlintConfigFile() }, root)
40 |
41 | fun VirtualFile.findNearestPackageJson(root: VirtualFile? = null): VirtualFile? =
42 | this.findNearestFile({ f -> f.isPackageJsonFile() }, root)
43 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/github/oxc/project/oxcintellijplugin/oxlint/settings/OxlintSettingsState.kt:
--------------------------------------------------------------------------------
1 | package com.github.oxc.project.oxcintellijplugin.oxlint.settings
2 |
3 | import com.github.oxc.project.oxcintellijplugin.ConfigurationMode
4 | import com.github.oxc.project.oxcintellijplugin.oxlint.OxlintRunTrigger
5 | import com.github.oxc.project.oxcintellijplugin.oxlint.OxlintUnusedDisableDirectivesSeverity
6 | import com.intellij.openapi.components.BaseState
7 | import com.intellij.util.xml.Attribute
8 | import com.intellij.util.xmlb.annotations.XMap
9 |
10 | class OxlintSettingsState : BaseState() {
11 |
12 | @get:Attribute("binaryPath")
13 | var binaryPath by string()
14 |
15 | @get:Attribute("binaryParameters")
16 | var binaryParameters by list()
17 |
18 | @get:Attribute("runTrigger")
19 | var runTrigger by enum(OxlintRunTrigger.ON_TYPE)
20 |
21 | @get:Attribute("configurationMode")
22 | var configurationMode by enum(ConfigurationMode.AUTOMATIC)
23 |
24 | @get:Attribute("configPath")
25 | var configPath by string()
26 |
27 | @get:Attribute("fixAllOnSave")
28 | var fixAllOnSave by property(false)
29 |
30 | @get:Attribute("flags")
31 | @get:XMap
32 | var flags by map()
33 |
34 | @get:Attribute("supportedExtensions")
35 | var supportedExtensions by list()
36 |
37 | @get:Attribute("typeAware")
38 | var typeAware by property(false)
39 |
40 | @get:Attribute("unusedDisableDirectives")
41 | var unusedDisableDirectives by enum(OxlintUnusedDisableDirectivesSeverity.ALLOW)
42 |
43 | companion object {
44 | val DEFAULT_EXTENSION_LIST = listOf(
45 | ".astro",
46 | ".js", ".jsx", ".cjs", ".mjs",
47 | ".svelte",
48 | ".ts", ".tsx", ".cts", ".mts",
49 | ".vue"
50 | )
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # IntelliJ Platform Artifacts Repositories -> https://plugins.jetbrains.com/docs/intellij/intellij-artifacts.html
2 |
3 | pluginGroup = com.github.oxc.project.oxcintellijplugin
4 | pluginName = Oxc
5 | pluginRepositoryUrl = https://github.com/oxc-project/oxc-intellij-plugin
6 | # SemVer format -> https://semver.org
7 | pluginVersion = 0.0.21
8 |
9 | # Supported build number ranges and IntelliJ Platform versions -> https://plugins.jetbrains.com/docs/intellij/build-number-ranges.html
10 | pluginSinceBuild = 251
11 | # This is unset so that we don't have to publish a new plugin version for every major.minor version of IntelliJ IDE.
12 | # https://github.com/oxc-project/oxc-intellij-plugin/issues/231
13 | # pluginUntilBuild =
14 |
15 | # IntelliJ Platform Properties -> https://plugins.jetbrains.com/docs/intellij/tools-gradle-intellij-plugin.html#configuration-intellij-extension
16 | platformType = WS
17 | platformVersion = 2025.1.3
18 |
19 | # Plugin Dependencies -> https://plugins.jetbrains.com/docs/intellij/plugin-dependencies.html
20 | # Example: platformPlugins = com.intellij.java, com.jetbrains.php:203.4449.22
21 | platformPlugins =
22 | # Example: platformBundledPlugins = com.intellij.java
23 | platformBundledPlugins = JavaScript
24 |
25 | # Opt-out flag for bundling Kotlin standard library -> https://jb.gg/intellij-platform-kotlin-stdlib
26 | kotlin.stdlib.default.dependency = false
27 |
28 | # Enable Gradle Configuration Cache -> https://docs.gradle.org/current/userguide/configuration_cache.html
29 | org.gradle.configuration-cache = true
30 |
31 | # Enable Gradle Build Cache -> https://docs.gradle.org/current/userguide/build_cache.html
32 | org.gradle.caching = true
33 |
34 | # Enable Gradle Kotlin DSL Lazy Property Assignment -> https://docs.gradle.org/current/userguide/kotlin_dsl.html#kotdsl:assignment
35 | systemProp.org.gradle.unsafe.kotlin.assignment = true
36 |
37 | kotlin.daemon.jvmargs=-Xmx1024M
38 |
--------------------------------------------------------------------------------
/.github/workflows/run-ui-tests.yml:
--------------------------------------------------------------------------------
1 | # GitHub Actions Workflow for launching UI tests on Linux, Windows, and Mac in the following steps:
2 | # - Prepare and launch IDE with your plugin and robot-server plugin, which is needed to interact with the UI.
3 | # - Wait for IDE to start.
4 | # - Run UI tests with a separate Gradle task.
5 | #
6 | # Please check https://github.com/JetBrains/intellij-ui-test-robot for information about UI tests with IntelliJ Platform.
7 | #
8 | # Workflow is triggered manually.
9 |
10 | permissions: {}
11 |
12 | name: Run UI Tests
13 | on:
14 | workflow_dispatch
15 |
16 | jobs:
17 |
18 | testUI:
19 | runs-on: ${{ matrix.os }}
20 | strategy:
21 | fail-fast: false
22 | matrix:
23 | include:
24 | - os: ubuntu-latest
25 | runIde: |
26 | export DISPLAY=:99.0
27 | Xvfb -ac :99 -screen 0 1920x1080x16 &
28 | gradle runIdeForUiTests &
29 | - os: windows-latest
30 | runIde: start gradlew.bat runIdeForUiTests
31 | - os: macos-latest
32 | runIde: ./gradlew runIdeForUiTests &
33 |
34 | steps:
35 |
36 | # Check out current repository
37 | - name: Fetch Sources
38 | uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
39 |
40 | # Set up Java environment for the next steps
41 | - name: Setup Java
42 | uses: actions/setup-java@f2beeb24e141e01a676f977032f5a29d81c9e27e # v5.1.0
43 | with:
44 | distribution: zulu
45 | java-version: 21
46 |
47 | # Setup Gradle
48 | - name: Setup Gradle
49 | uses: gradle/actions/setup-gradle@4d9f0ba0025fe599b4ebab900eb7f3a1d93ef4c2 # v5.0.0
50 | with:
51 | cache-read-only: ${{ github.ref != 'refs/heads/main' }}
52 |
53 | # Run IDEA prepared for UI testing
54 | - name: Run IDE
55 | run: ${{ matrix.runIde }}
56 |
57 | # Wait for IDEA to be started
58 | - name: Health Check
59 | uses: jtalk/url-health-check-action@v4
60 | with:
61 | url: http://127.0.0.1:8082
62 | max-attempts: 15
63 | retry-delay: 30s
64 |
65 | # Run tests
66 | - name: Tests
67 | run: ./gradlew test
68 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/github/oxc/project/oxcintellijplugin/oxlint/lsp/OxlintLspServerSupportProvider.kt:
--------------------------------------------------------------------------------
1 | package com.github.oxc.project.oxcintellijplugin.oxlint.lsp
2 |
3 | import com.github.oxc.project.oxcintellijplugin.OxcIcons
4 | import com.github.oxc.project.oxcintellijplugin.oxlint.OxlintPackage
5 | import com.github.oxc.project.oxcintellijplugin.oxlint.settings.OxlintConfigurable
6 | import com.github.oxc.project.oxcintellijplugin.oxlint.settings.OxlintSettings
7 | import com.intellij.openapi.diagnostic.thisLogger
8 | import com.intellij.openapi.project.Project
9 | import com.intellij.openapi.roots.ProjectRootManager
10 | import com.intellij.openapi.vfs.VirtualFile
11 | import com.intellij.openapi.vfs.VirtualFileManager
12 | import com.intellij.platform.lsp.api.LspServer
13 | import com.intellij.platform.lsp.api.LspServerSupportProvider
14 | import com.intellij.platform.lsp.api.lsWidget.LspServerWidgetItem
15 | import kotlin.io.path.Path
16 |
17 | class OxlintLspServerSupportProvider : LspServerSupportProvider {
18 | override fun fileOpened(project: Project,
19 | file: VirtualFile,
20 | serverStarter: LspServerSupportProvider.LspServerStarter) {
21 | thisLogger().debug("Handling fileOpened for ${file.path}")
22 |
23 | if (!OxlintSettings.getInstance(project).fileSupported(file)) {
24 | return
25 | }
26 |
27 | val oxc = OxlintPackage(project)
28 | if (!oxc.isEnabled()) {
29 | return
30 | }
31 | val executable = oxc.binaryPath(file) ?: return
32 | val nodePackage = oxc.getPackage(file)
33 | val root = if (nodePackage != null) {
34 | VirtualFileManager.getInstance().findFileByNioPath(Path(nodePackage.systemIndependentPath))?.parent?.parent ?: return
35 | } else {
36 | ProjectRootManager.getInstance(project).fileIndex.getContentRootForFile(file) ?: return
37 | }
38 |
39 | serverStarter.ensureServerStarted(OxlintLspServerDescriptor(project, root, executable, oxc.binaryParameters(file)))
40 | }
41 |
42 | override fun createLspServerWidgetItem(lspServer: LspServer,
43 | currentFile: VirtualFile?): LspServerWidgetItem {
44 | return LspServerWidgetItem(lspServer, currentFile, OxcIcons.OxcRound, OxlintConfigurable::class.java)
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/github/oxc/project/oxcintellijplugin/oxfmt/lsp/OxfmtLspServerSupportProvider.kt:
--------------------------------------------------------------------------------
1 | package com.github.oxc.project.oxcintellijplugin.oxfmt.lsp
2 |
3 | import com.github.oxc.project.oxcintellijplugin.OxcIcons
4 | import com.github.oxc.project.oxcintellijplugin.oxfmt.OxfmtPackage
5 | import com.github.oxc.project.oxcintellijplugin.oxfmt.settings.OxfmtConfigurable
6 | import com.github.oxc.project.oxcintellijplugin.oxfmt.settings.OxfmtSettings
7 | import com.intellij.openapi.diagnostic.thisLogger
8 | import com.intellij.openapi.project.Project
9 | import com.intellij.openapi.roots.ProjectRootManager
10 | import com.intellij.openapi.vfs.VirtualFile
11 | import com.intellij.openapi.vfs.VirtualFileManager
12 | import com.intellij.platform.lsp.api.LspServer
13 | import com.intellij.platform.lsp.api.LspServerSupportProvider
14 | import com.intellij.platform.lsp.api.lsWidget.LspServerWidgetItem
15 | import kotlin.io.path.Path
16 |
17 | class OxfmtLspServerSupportProvider : LspServerSupportProvider {
18 |
19 | override fun fileOpened(project: Project,
20 | file: VirtualFile,
21 | serverStarter: LspServerSupportProvider.LspServerStarter) {
22 | thisLogger().debug("Handling fileOpened for ${file.path}")
23 |
24 | if (!OxfmtSettings.getInstance(project).fileSupported(file)) {
25 | return
26 | }
27 |
28 | val oxfmt = OxfmtPackage(project)
29 | if (!oxfmt.isEnabled()) {
30 | return
31 | }
32 | val executable = oxfmt.binaryPath(file) ?: return
33 | val nodePackage = oxfmt.getPackage(file)
34 | val root = if (nodePackage != null) {
35 | VirtualFileManager.getInstance()
36 | .findFileByNioPath(Path(nodePackage.systemIndependentPath))?.parent?.parent
37 | ?: return
38 | } else {
39 | ProjectRootManager.getInstance(project).fileIndex.getContentRootForFile(file) ?: return
40 | }
41 |
42 | serverStarter.ensureServerStarted(
43 | OxfmtLspServerDescriptor(project, root, executable, oxfmt.binaryParameters(file)))
44 | }
45 |
46 | override fun createLspServerWidgetItem(lspServer: LspServer,
47 | currentFile: VirtualFile?): LspServerWidgetItem {
48 | return LspServerWidgetItem(lspServer, currentFile, OxcIcons.OxcRound,
49 | OxfmtConfigurable::class.java)
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/github/oxc/project/oxcintellijplugin/oxlint/settings/OxlintOnSaveFixAllActionInfo.kt:
--------------------------------------------------------------------------------
1 | package com.github.oxc.project.oxcintellijplugin.oxlint.settings
2 |
3 | import com.github.oxc.project.oxcintellijplugin.ConfigurationMode
4 | import com.github.oxc.project.oxcintellijplugin.oxlint.OxlintBundle
5 | import com.intellij.ide.actionsOnSave.ActionOnSaveBackedByOwnConfigurable
6 | import com.intellij.ide.actionsOnSave.ActionOnSaveComment
7 | import com.intellij.ide.actionsOnSave.ActionOnSaveContext
8 |
9 | class OxlintOnSaveFixAllActionInfo(actionOnSaveContext: ActionOnSaveContext) :
10 | ActionOnSaveBackedByOwnConfigurable(actionOnSaveContext,
11 | OxlintConfigurable.CONFIGURABLE_ID,
12 | OxlintConfigurable::class.java) {
13 |
14 | override fun getActionOnSaveName() =
15 | OxlintBundle.message("oxlint.run.fix.all.on.save.checkbox.on.actions.on.save.page")
16 |
17 | override fun isApplicableAccordingToStoredState(): Boolean =
18 | OxlintSettings.getInstance(project).configurationMode != ConfigurationMode.DISABLED
19 |
20 | override fun isApplicableAccordingToUiState(configurable: OxlintConfigurable): Boolean =
21 | !configurable.disabledConfiguration.isSelected
22 |
23 | override fun isActionOnSaveEnabledAccordingToStoredState() = OxlintSettings.getInstance(project).fixAllOnSave
24 |
25 | override fun isActionOnSaveEnabledAccordingToUiState(configurable: OxlintConfigurable) =
26 | configurable.fixAllOnSaveCheckBox.isSelected
27 |
28 | override fun setActionOnSaveEnabled(configurable: OxlintConfigurable,
29 | enabled: Boolean) {
30 | configurable.fixAllOnSaveCheckBox.isSelected = enabled
31 | }
32 |
33 | override fun getCommentAccordingToUiState(configurable: OxlintConfigurable): ActionOnSaveComment? {
34 | return comment()
35 | }
36 |
37 | override fun getCommentAccordingToStoredState(): ActionOnSaveComment? {
38 | return comment()
39 | }
40 |
41 | override fun getActionLinks() = listOf(createGoToPageInSettingsLink(OxlintConfigurable.CONFIGURABLE_ID))
42 |
43 | private fun comment(): ActionOnSaveComment? {
44 | if (!isSaveActionApplicable) return ActionOnSaveComment.info(OxlintBundle.message("oxlint.on.save.comment.disabled"))
45 |
46 | return ActionOnSaveComment.info(OxlintBundle.message("oxlint.on.save.comment.fix.all"))
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/github/oxc/project/oxcintellijplugin/oxfmt/settings/OxfmtOnSaveFixAllActionInfo.kt:
--------------------------------------------------------------------------------
1 | package com.github.oxc.project.oxcintellijplugin.oxfmt.settings
2 |
3 | import com.github.oxc.project.oxcintellijplugin.ConfigurationMode
4 | import com.github.oxc.project.oxcintellijplugin.oxfmt.OxfmtBundle
5 | import com.intellij.ide.actionsOnSave.ActionOnSaveBackedByOwnConfigurable
6 | import com.intellij.ide.actionsOnSave.ActionOnSaveComment
7 | import com.intellij.ide.actionsOnSave.ActionOnSaveContext
8 |
9 | class OxfmtOnSaveFixAllActionInfo(actionOnSaveContext: ActionOnSaveContext) :
10 | ActionOnSaveBackedByOwnConfigurable(actionOnSaveContext,
11 | OxfmtConfigurable.CONFIGURABLE_ID, OxfmtConfigurable::class.java) {
12 |
13 | override fun getActionOnSaveName() =
14 | OxfmtBundle.message("oxfmt.run.fix.all.on.save.checkbox.on.actions.on.save.page")
15 |
16 | override fun isApplicableAccordingToStoredState(): Boolean =
17 | OxfmtSettings.getInstance(project).configurationMode != ConfigurationMode.DISABLED
18 |
19 | override fun isApplicableAccordingToUiState(configurable: OxfmtConfigurable): Boolean =
20 | !configurable.disabledConfiguration.isSelected
21 |
22 | override fun isActionOnSaveEnabledAccordingToStoredState() =
23 | OxfmtSettings.getInstance(project).fixAllOnSave
24 |
25 | override fun isActionOnSaveEnabledAccordingToUiState(configurable: OxfmtConfigurable) =
26 | configurable.fixAllOnSaveCheckBox.isSelected
27 |
28 | override fun setActionOnSaveEnabled(configurable: OxfmtConfigurable, enabled: Boolean) {
29 | configurable.fixAllOnSaveCheckBox.isSelected = enabled
30 | }
31 |
32 | override fun getCommentAccordingToUiState(
33 | configurable: OxfmtConfigurable): ActionOnSaveComment? {
34 | return comment()
35 | }
36 |
37 | override fun getCommentAccordingToStoredState(): ActionOnSaveComment? {
38 | return comment()
39 | }
40 |
41 | override fun getActionLinks() =
42 | listOf(createGoToPageInSettingsLink(OxfmtConfigurable.CONFIGURABLE_ID))
43 |
44 | private fun comment(): ActionOnSaveComment? {
45 | if (!isSaveActionApplicable) return ActionOnSaveComment.info(
46 | OxfmtBundle.message("oxfmt.on.save.comment.disabled"))
47 |
48 | return ActionOnSaveComment.info(OxfmtBundle.message("oxfmt.on.save.comment.fix.all"))
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/main/resources/messages/OxlintBundle.properties:
--------------------------------------------------------------------------------
1 | oxlint.config.path.label=Path to Oxlint Config:
2 | oxlint.configure.extensions.link=Configure file extensions
3 | oxlint.file.not.supported.description=The file "{0}" is not supported by Oxlint.
4 | oxlint.file.not.supported.title=Unsupported file
5 | oxlint.fix.all.failure.description=An error occurred while applying 'Fix All': {0}
6 | oxlint.fix.all.failure.label=Failed to Apply 'Fix All'
7 | oxlint.fix.all.on.save.checkbox.on.actions.on.save.page=Run 'Fix All' with Oxlint on Save
8 | oxlint.fix.all.on.save.failure.description=An error occurred while running 'Fix All' on save: {0}
9 | oxlint.fix.all.on.save.failure.label=Failed to apply 'Fix all' on save
10 | oxlint.fix.all.success.description='Fix All' successfully applied.
11 | oxlint.fix.all.success.label=Fixes applied successfully
12 | oxlint.language.server.not.found=Oxlint Language Server not found.
13 | oxlint.language.server.restarted=Oxlint language server restarted.
14 | oxlint.name=Oxlint
15 | oxlint.on.save.comment.disabled=Oxlint integration is disabled.
16 | oxlint.on.save.comment.fix.all=Oxlint 'Fix All'
17 | oxlint.run.fix.all=Running Oxlint 'Fix All'
18 | oxlint.run.fix.all.on.save.checkbox.on.actions.on.save.page=Run Oxlint 'Fix All' on Save
19 | oxlint.run.fix.all.on.save.label=Run 'Fix All' on &Save
20 | oxlint.run.quickfix=Running Oxlint Quickfix
21 | oxlint.schema.name=Oxlint
22 | oxlint.settings.languageServerPath=Path to Oxlint Language Server:
23 | oxlint.settings.oxlintRunTrigger=Oxlint Execution Trigger:
24 | oxlint.settings.unusedDisableDirectives=Unused Disable Directives:
25 | oxlint.supported.extensions.comment=Enter file extensions separated by commas (e.g., .js, .ts, .jsx). Each extension must start with '.' and contain only alphanumeric characters.
26 | oxlint.supported.extensions.label=Supported File Extensions:
27 | oxlint.type.aware.label=Enable type aware rules
28 | oxlint.diagnostic.unknown.code=Unknown Code
29 | oxlint.disable.nested.config.label=Disable nested config lookups
30 | oxlint.fix.kind.label=Fix Kind
31 | oxlint.manual.add.lsp.argument=Add --lsp CLI argument
32 | oxlint.manual.add.lsp.argument.help=Oxlint 1.29.0 or newer had a change in architecture. As a result when manually configuring the LSP path to the `oxlint` executable (not the `oxc_language_server` executable), the IDE needs to know when to supply the new required --lsp flag. Checking this box will cause that flag to be supplied.
33 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/github/oxc/project/oxcintellijplugin/oxfmt/actions/OxfmtFixAllOnSaveAction.kt:
--------------------------------------------------------------------------------
1 | package com.github.oxc.project.oxcintellijplugin.oxfmt.actions
2 |
3 | import com.github.oxc.project.oxcintellijplugin.NOTIFICATION_GROUP
4 | import com.github.oxc.project.oxcintellijplugin.oxfmt.OxfmtBundle
5 | import com.github.oxc.project.oxcintellijplugin.oxfmt.services.OxfmtServerService
6 | import com.github.oxc.project.oxcintellijplugin.oxfmt.settings.OxfmtSettings
7 | import com.intellij.ide.actionsOnSave.impl.ActionsOnSaveFileDocumentManagerListener
8 | import com.intellij.notification.NotificationGroupManager
9 | import com.intellij.notification.NotificationType
10 | import com.intellij.openapi.editor.Document
11 | import com.intellij.openapi.fileEditor.FileDocumentManager
12 | import com.intellij.openapi.project.Project
13 | import com.intellij.platform.ide.progress.runWithModalProgressBlocking
14 | import kotlinx.coroutines.withTimeout
15 |
16 | class OxfmtFixAllOnSaveAction : ActionsOnSaveFileDocumentManagerListener.ActionOnSave() {
17 |
18 | override fun isEnabledForProject(project: Project): Boolean {
19 | return OxfmtSettings.getInstance(project).fixAllOnSave
20 | }
21 |
22 | override fun processDocuments(project: Project, documents: Array) {
23 | val notificationGroup = NotificationGroupManager.getInstance().getNotificationGroup(NOTIFICATION_GROUP)
24 |
25 | runWithModalProgressBlocking(project, OxfmtBundle.message("oxfmt.run.fix.all")) {
26 | try {
27 | withTimeout(5_000) {
28 | documents.filter {
29 | val settings = OxfmtSettings.getInstance(project)
30 | val manager = FileDocumentManager.getInstance()
31 | val virtualFile = manager.getFile(it) ?: return@filter false
32 | return@filter settings.fileSupported(virtualFile)
33 | }.forEach {
34 | OxfmtServerService.getInstance(project).fixAll(it)
35 | }
36 | }
37 | } catch (e: Exception) {
38 | notificationGroup.createNotification(
39 | title = OxfmtBundle.message("oxfmt.fix.all.on.save.failure.label"),
40 | content = OxfmtBundle.message("oxfmt.fix.all.on.save.failure.description",
41 | e.message.toString()), type = NotificationType.ERROR).notify(project)
42 | }
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/main/resources/icons/oxcRound.svg:
--------------------------------------------------------------------------------
1 |
15 |
--------------------------------------------------------------------------------
/src/main/resources/META-INF/pluginIcon.svg:
--------------------------------------------------------------------------------
1 |
15 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/github/oxc/project/oxcintellijplugin/oxlint/actions/OxlintFixAllOnSaveAction.kt:
--------------------------------------------------------------------------------
1 | package com.github.oxc.project.oxcintellijplugin.oxlint.actions
2 |
3 | import com.github.oxc.project.oxcintellijplugin.NOTIFICATION_GROUP
4 | import com.github.oxc.project.oxcintellijplugin.oxlint.OxlintBundle
5 | import com.github.oxc.project.oxcintellijplugin.oxlint.services.OxlintServerService
6 | import com.github.oxc.project.oxcintellijplugin.oxlint.settings.OxlintSettings
7 | import com.intellij.ide.actionsOnSave.impl.ActionsOnSaveFileDocumentManagerListener
8 | import com.intellij.notification.NotificationGroupManager
9 | import com.intellij.notification.NotificationType
10 | import com.intellij.openapi.editor.Document
11 | import com.intellij.openapi.fileEditor.FileDocumentManager
12 | import com.intellij.openapi.project.Project
13 | import com.intellij.platform.ide.progress.runWithModalProgressBlocking
14 | import kotlinx.coroutines.withTimeout
15 |
16 | class OxlintFixAllOnSaveAction : ActionsOnSaveFileDocumentManagerListener.ActionOnSave() {
17 | override fun isEnabledForProject(project: Project): Boolean {
18 | return OxlintSettings.getInstance(project).fixAllOnSave
19 | }
20 |
21 | override fun processDocuments(project: Project,
22 | documents: Array) {
23 | val notificationGroup = NotificationGroupManager.getInstance().getNotificationGroup(NOTIFICATION_GROUP)
24 |
25 | runWithModalProgressBlocking(project,
26 | OxlintBundle.message("oxlint.run.fix.all")) {
27 | try {
28 | withTimeout(5_000) {
29 | documents.filter {
30 | val settings = OxlintSettings.getInstance(project)
31 | val manager = FileDocumentManager.getInstance()
32 | val virtualFile = manager.getFile(it) ?: return@filter false
33 | return@filter settings.fileSupported(virtualFile)
34 | }.forEach {
35 | OxlintServerService.getInstance(project).fixAll(it)
36 | }
37 | }
38 | } catch (e: Exception) {
39 | notificationGroup.createNotification(
40 | title = OxlintBundle.message("oxlint.fix.all.on.save.failure.label"),
41 | content = OxlintBundle.message(
42 | "oxlint.fix.all.on.save.failure.description",
43 | e.message.toString()
44 | ),
45 | type = NotificationType.ERROR).notify(project)
46 | }
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/github/oxc/project/oxcintellijplugin/NodeProcessCommandBuilder.kt:
--------------------------------------------------------------------------------
1 | package com.github.oxc.project.oxcintellijplugin
2 |
3 | import com.github.oxc.project.oxcintellijplugin.oxlint.OxlintBundle
4 | import com.intellij.execution.ExecutionException
5 | import com.intellij.execution.target.value.TargetValue
6 | import com.intellij.javascript.nodejs.execution.NodeTargetRun
7 | import com.intellij.javascript.nodejs.execution.NodeTargetRunOptions.Companion.of
8 | import com.intellij.javascript.nodejs.interpreter.NodeJsInterpreter
9 | import com.intellij.openapi.project.Project
10 | import com.intellij.openapi.vfs.VirtualFile
11 | import java.nio.charset.Charset
12 |
13 | class NodeProcessCommandBuilder(
14 | project: Project,
15 | interpreter: NodeJsInterpreter,
16 | ) : ProcessCommandBuilder {
17 |
18 | private val target = NodeTargetRun(interpreter, project, null, of(false))
19 | private val builder = target.commandLineBuilder
20 | private var executable: String? = null
21 | private var workingDir: String? = null
22 | private var inputFile: VirtualFile? = null
23 | private var charset: Charset? = null
24 | private val parameters = mutableListOf()
25 |
26 | override fun setWorkingDirectory(path: String?): ProcessCommandBuilder {
27 | workingDir = path
28 | return this
29 | }
30 |
31 | override fun setInputFile(file: VirtualFile?): ProcessCommandBuilder {
32 | inputFile = file
33 | return this
34 | }
35 |
36 | override fun addParameters(params: List): ProcessCommandBuilder {
37 | parameters.addAll(params.map {
38 | when (it) {
39 | is ProcessCommandParameter.Value -> it.value
40 | is ProcessCommandParameter.FilePath -> target.convertLocalPathToTargetPath(it.path.toString())
41 | }
42 | })
43 | return this
44 | }
45 |
46 | override fun setExecutable(executable: String): ProcessCommandBuilder {
47 | this.executable = executable
48 | return this
49 | }
50 |
51 | override fun setCharset(charset: Charset): ProcessCommandBuilder {
52 | this.charset = charset
53 | return this
54 | }
55 |
56 | override fun build(): OxcTargetRun {
57 | val exec = executable ?: throw ExecutionException(OxlintBundle.message("oxlint.language.server.not.found"))
58 |
59 | workingDir?.let { builder.setWorkingDirectory(target.path(it)) }
60 | builder.addParameter(target.path(exec))
61 | parameters.forEach { builder.addParameter(it) }
62 | inputFile?.let { builder.setInputFile(TargetValue.fixed(it.path)) }
63 | charset?.let { builder.charset = it }
64 |
65 | return OxcTargetRun.Node(target)
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/github/oxc/project/oxcintellijplugin/oxfmt/settings/OxfmtSettings.kt:
--------------------------------------------------------------------------------
1 | package com.github.oxc.project.oxcintellijplugin.oxfmt.settings
2 |
3 | import com.github.oxc.project.oxcintellijplugin.ConfigurationMode
4 | import com.github.oxc.project.oxcintellijplugin.oxfmt.settings.OxfmtSettingsState.Companion.DEFAULT_EXTENSION_LIST
5 | import com.intellij.openapi.components.Service
6 | import com.intellij.openapi.components.SettingsCategory
7 | import com.intellij.openapi.components.SimplePersistentStateComponent
8 | import com.intellij.openapi.components.State
9 | import com.intellij.openapi.components.Storage
10 | import com.intellij.openapi.components.service
11 | import com.intellij.openapi.project.Project
12 | import com.intellij.openapi.vfs.VirtualFile
13 | import java.io.File
14 |
15 | @Service(Service.Level.PROJECT)
16 | @State(name = "OxfmtSettings", storages = [Storage("OxfmtSettings.xml")],
17 | category = SettingsCategory.TOOLS)
18 | class OxfmtSettings(private val project: Project) :
19 | SimplePersistentStateComponent(OxfmtSettingsState()) {
20 |
21 | var configurationMode: ConfigurationMode
22 | get() = state.configurationMode
23 | set(value) {
24 | state.configurationMode = value
25 | }
26 |
27 | var supportedExtensions: MutableList
28 | get() = state.supportedExtensions.takeIf { it.isNotEmpty() }
29 | ?: DEFAULT_EXTENSION_LIST.toMutableList()
30 | set(value) {
31 | state.supportedExtensions.clear()
32 | state.supportedExtensions.addAll(value)
33 | }
34 |
35 | var binaryPath: String
36 | get() = state.binaryPath ?: ""
37 | set(value) {
38 | state.binaryPath = value
39 | }
40 |
41 | var configPath: String
42 | get() = state.configPath ?: ""
43 | set(value) {
44 | val file = File(value)
45 | if (file.isFile) {
46 | state.configPath = file.path
47 | return
48 | }
49 | state.configPath = value
50 | }
51 |
52 | var fixAllOnSave: Boolean
53 | get() = isEnabled() && state.fixAllOnSave
54 | set(value) {
55 | state.fixAllOnSave = value
56 | }
57 |
58 | fun isEnabled(): Boolean {
59 | return configurationMode !== ConfigurationMode.DISABLED
60 | }
61 |
62 | fun fileSupported(file: VirtualFile): Boolean {
63 | val fileExtension = file.extension
64 | return if (fileExtension != null) {
65 | supportedExtensions.contains(".$fileExtension")
66 | } else {
67 | false
68 | }
69 | }
70 |
71 | companion object {
72 |
73 | @JvmStatic
74 | fun getInstance(project: Project): OxfmtSettings = project.service()
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 | @rem SPDX-License-Identifier: Apache-2.0
17 | @rem
18 |
19 | @if "%DEBUG%"=="" @echo off
20 | @rem ##########################################################################
21 | @rem
22 | @rem Gradle startup script for Windows
23 | @rem
24 | @rem ##########################################################################
25 |
26 | @rem Set local scope for the variables with windows NT shell
27 | if "%OS%"=="Windows_NT" setlocal
28 |
29 | set DIRNAME=%~dp0
30 | if "%DIRNAME%"=="" set DIRNAME=.
31 | @rem This is normally unused
32 | set APP_BASE_NAME=%~n0
33 | set APP_HOME=%DIRNAME%
34 |
35 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
36 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
37 |
38 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
39 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
40 |
41 | @rem Find java.exe
42 | if defined JAVA_HOME goto findJavaFromJavaHome
43 |
44 | set JAVA_EXE=java.exe
45 | %JAVA_EXE% -version >NUL 2>&1
46 | if %ERRORLEVEL% equ 0 goto execute
47 |
48 | echo. 1>&2
49 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
50 | echo. 1>&2
51 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2
52 | echo location of your Java installation. 1>&2
53 |
54 | goto fail
55 |
56 | :findJavaFromJavaHome
57 | set JAVA_HOME=%JAVA_HOME:"=%
58 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
59 |
60 | if exist "%JAVA_EXE%" goto execute
61 |
62 | echo. 1>&2
63 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
64 | echo. 1>&2
65 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2
66 | echo location of your Java installation. 1>&2
67 |
68 | goto fail
69 |
70 | :execute
71 | @rem Setup the command line
72 |
73 |
74 |
75 | @rem Execute Gradle
76 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
77 |
78 | :end
79 | @rem End local scope for the variables with windows NT shell
80 | if %ERRORLEVEL% equ 0 goto mainEnd
81 |
82 | :fail
83 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
84 | rem the _cmd.exe /c_ return code!
85 | set EXIT_CODE=%ERRORLEVEL%
86 | if %EXIT_CODE% equ 0 set EXIT_CODE=1
87 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
88 | exit /b %EXIT_CODE%
89 |
90 | :mainEnd
91 | if "%OS%"=="Windows_NT" endlocal
92 |
93 | :omega
94 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/github/oxc/project/oxcintellijplugin/GeneralProcessCommandBuilder.kt:
--------------------------------------------------------------------------------
1 | package com.github.oxc.project.oxcintellijplugin
2 |
3 | import com.github.oxc.project.oxcintellijplugin.oxlint.OxlintBundle
4 | import com.intellij.execution.ExecutionException
5 | import com.intellij.execution.configurations.GeneralCommandLine
6 | import com.intellij.execution.wsl.WSLCommandLineOptions
7 | import com.intellij.execution.wsl.WslPath
8 | import com.intellij.execution.wsl.getWslPathSafe
9 | import com.intellij.openapi.vfs.VirtualFile
10 | import java.io.File
11 | import java.nio.charset.Charset
12 | import kotlin.io.path.Path
13 |
14 | class GeneralProcessCommandBuilder : ProcessCommandBuilder {
15 | private val command =
16 | GeneralCommandLine().withParentEnvironmentType(GeneralCommandLine.ParentEnvironmentType.CONSOLE)
17 | private var executable: String? = null
18 | private var workingDir: String? = null
19 | private var inputFile: VirtualFile? = null
20 | private var charset: Charset? = null
21 | private val parameters = mutableListOf()
22 |
23 | override fun setWorkingDirectory(path: String?): ProcessCommandBuilder {
24 | workingDir = path
25 | return this
26 | }
27 |
28 | override fun setInputFile(file: VirtualFile?): ProcessCommandBuilder {
29 | inputFile = file
30 | return this
31 | }
32 |
33 | override fun addParameters(params: List): ProcessCommandBuilder {
34 | parameters.addAll(params)
35 | return this
36 | }
37 |
38 | override fun setExecutable(executable: String): ProcessCommandBuilder {
39 | this.executable = executable
40 | return this
41 | }
42 |
43 | override fun setCharset(charset: Charset): ProcessCommandBuilder {
44 | this.charset = charset
45 | return this
46 | }
47 |
48 | override fun build(): OxcTargetRun {
49 | val exec = executable ?: throw ExecutionException(OxlintBundle.message("oxlint.language.server.not.found"))
50 |
51 | command.withExePath(exec)
52 | workingDir?.let { command.withWorkingDirectory(Path(it)) }
53 | inputFile?.let { command.withInput(File(it.path)) }
54 | charset?.let { command.withCharset(it) }
55 |
56 | // patch the command line to be run on WSL 2 instead of the host
57 | val wslDistribution = WslPath.getDistributionByWindowsUncPath(exec)
58 | if (wslDistribution != null) {
59 | command.withExePath(wslDistribution.getWslPathSafe(Path(exec)))
60 |
61 | // add command parameters, converting file paths if needed
62 | command.addParameters(parameters.map {
63 | when (it) {
64 | is ProcessCommandParameter.Value -> it.value
65 | is ProcessCommandParameter.FilePath -> wslDistribution.getWslPathSafe(it.path)
66 | }
67 | })
68 |
69 | val options = WSLCommandLineOptions().apply {
70 | isPassEnvVarsUsingInterop = true
71 |
72 | workingDir?.let {
73 | remoteWorkingDirectory = wslDistribution.getWslPathSafe(Path(it))
74 | }
75 | }
76 |
77 | wslDistribution.patchCommandLine(command, null, options)
78 | } else {
79 | command.addParameters(parameters.map { it.toString() })
80 | }
81 |
82 | return OxcTargetRun.General(command, wslDistribution)
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/github/oxc/project/oxcintellijplugin/oxfmt/services/OxfmtServerService.kt:
--------------------------------------------------------------------------------
1 | package com.github.oxc.project.oxcintellijplugin.oxfmt.services
2 |
3 | import com.github.oxc.project.oxcintellijplugin.NOTIFICATION_GROUP
4 | import com.github.oxc.project.oxcintellijplugin.oxfmt.OxfmtBundle
5 | import com.github.oxc.project.oxcintellijplugin.oxfmt.lsp.OxfmtLspServerSupportProvider
6 | import com.intellij.application.options.CodeStyle
7 | import com.intellij.notification.NotificationGroupManager
8 | import com.intellij.notification.NotificationType
9 | import com.intellij.openapi.command.WriteCommandAction
10 | import com.intellij.openapi.components.Service
11 | import com.intellij.openapi.editor.Document
12 | import com.intellij.openapi.fileEditor.FileDocumentManager
13 | import com.intellij.openapi.project.Project
14 | import com.intellij.openapi.vfs.VirtualFile
15 | import com.intellij.platform.lsp.api.LspServerManager
16 | import org.eclipse.lsp4j.DocumentFormattingParams
17 | import org.eclipse.lsp4j.FormattingOptions
18 |
19 | @Service(Service.Level.PROJECT)
20 | class OxfmtServerService(private val project: Project) {
21 |
22 | private val PROVIDER_CLASS = OxfmtLspServerSupportProvider::class.java
23 | private val GROUP_ID = "Oxc"
24 |
25 | companion object {
26 |
27 | fun getInstance(project: Project): OxfmtServerService =
28 | project.getService(OxfmtServerService::class.java)
29 | }
30 |
31 | private fun getServer(file: VirtualFile) =
32 | LspServerManager.getInstance(project).getServersForProvider(PROVIDER_CLASS)
33 | .firstOrNull { server -> server.descriptor.isSupportedFile(file) }
34 |
35 | suspend fun fixAll(document: Document) {
36 | val manager = FileDocumentManager.getInstance()
37 | val file = manager.getFile(document) ?: return
38 |
39 | fixAll(file, document)
40 | }
41 |
42 | suspend fun fixAll(file: VirtualFile, document: Document) {
43 | val server = getServer(file) ?: return
44 | val codeStyleSettings = CodeStyle.getSettings(project, document)
45 | val indentOptions = codeStyleSettings.getIndentOptionsByDocument(project, document)
46 |
47 | val documentFormattingParams = DocumentFormattingParams(server.getDocumentIdentifier(file),
48 | FormattingOptions(indentOptions.INDENT_SIZE, !indentOptions.USE_TAB_CHARACTER))
49 |
50 | val formattingResults = server.sendRequest {
51 | it.textDocumentService.formatting(documentFormattingParams)
52 | }
53 |
54 | WriteCommandAction.runWriteCommandAction(project, OxfmtBundle.message("oxfmt.run.quickfix"),
55 | GROUP_ID, {
56 | formattingResults?.forEach {
57 | val startLineOffset = document.getLineStartOffset(it.range.start.line)
58 | val endLineOffset = document.getLineStartOffset(it.range.end.line)
59 | document.replaceString(startLineOffset + it.range.start.character,
60 | endLineOffset + it.range.end.character, it.newText)
61 | }
62 | })
63 | }
64 |
65 | fun restartServer() {
66 | LspServerManager.getInstance(project).stopAndRestartIfNeeded(PROVIDER_CLASS)
67 | }
68 |
69 | fun stopServer() {
70 | LspServerManager.getInstance(project).stopServers(PROVIDER_CLASS)
71 | }
72 |
73 | fun notifyRestart() {
74 | NotificationGroupManager.getInstance().getNotificationGroup(NOTIFICATION_GROUP)
75 | .createNotification(OxfmtBundle.message("oxfmt.language.server.restarted"), "",
76 | NotificationType.INFORMATION).notify(project)
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/src/test/kotlin/com/github/oxc/project/oxcintellijplugin/extensions/TestCaseExt.kt:
--------------------------------------------------------------------------------
1 | package com.github.oxc.project.oxcintellijplugin.extensions
2 |
3 | import com.intellij.openapi.project.Project
4 | import com.intellij.openapi.util.Disposer
5 | import com.intellij.openapi.vfs.VirtualFile
6 | import com.intellij.platform.lsp.api.LspServer
7 | import com.intellij.platform.lsp.api.LspServerDescriptor
8 | import com.intellij.platform.lsp.api.LspServerManager
9 | import com.intellij.platform.lsp.api.LspServerManagerListener
10 | import com.intellij.platform.lsp.api.LspServerState
11 | import com.intellij.testFramework.ExpectedHighlightingData
12 | import com.intellij.testFramework.PlatformTestUtil
13 | import com.intellij.testFramework.fixtures.CodeInsightTestFixture
14 | import com.intellij.testFramework.fixtures.impl.CodeInsightTestFixtureImpl
15 | import com.intellij.tool.withRetryAsync
16 | import java.util.concurrent.atomic.AtomicBoolean
17 | import kotlin.time.Duration
18 | import kotlin.time.Duration.Companion.seconds
19 | import kotlinx.coroutines.runBlocking
20 |
21 | fun CodeInsightTestFixture.configureByFileAndCheckLanguageServerHighlighting(lspServerDescriptorClass: Class, filePath: String) {
22 | val configuredFile = this.configureByFile(filePath).virtualFile
23 | val project = this.project
24 | val fixture = this
25 |
26 | runBlocking {
27 | withRetryAsync(retries = 3, delayBetweenRetries = 1.seconds, retryAction = {
28 | val expectedHighlightingData = ExpectedHighlightingData(editor.document, true, true,
29 | true, false)
30 | expectedHighlightingData.init()
31 | waitForLanguageServerDiagnostics(project, lspServerDescriptorClass, configuredFile)
32 |
33 | (fixture as CodeInsightTestFixtureImpl).collectAndCheckHighlighting(
34 | expectedHighlightingData)
35 | })
36 | }
37 | }
38 |
39 | private fun waitForLanguageServerDiagnostics(project: Project, lspServerDescriptorClass: Class,
40 | fileNeedingDiagnostics: VirtualFile, timeout: Duration = 10.seconds) {
41 | val diagnosticsReceived = AtomicBoolean()
42 | val shutdownReceived = AtomicBoolean()
43 |
44 | val disposable = Disposer.newDisposable()
45 | try {
46 | LspServerManager.getInstance(project)
47 | .addLspServerManagerListener(object : LspServerManagerListener {
48 | override fun serverStateChanged(lspServer: LspServer) {
49 | if (!lspServerDescriptorClass.isInstance(lspServer.descriptor)) {
50 | return
51 | }
52 | if (lspServer.state in arrayOf(LspServerState.ShutdownNormally,
53 | LspServerState.ShutdownUnexpectedly)
54 | ) {
55 | shutdownReceived.set(true)
56 | }
57 | }
58 |
59 | override fun diagnosticsReceived(lspServer: LspServer, file: VirtualFile) {
60 | if (!lspServerDescriptorClass.isInstance(lspServer.descriptor)) {
61 | return
62 | }
63 | if (file != fileNeedingDiagnostics) {
64 | return
65 | }
66 | diagnosticsReceived.set(true)
67 | }
68 | }, disposable, true)
69 |
70 | PlatformTestUtil.waitWithEventsDispatching(
71 | "Timeout waiting for diagnostics for $fileNeedingDiagnostics",
72 | { diagnosticsReceived.get() || shutdownReceived.get() }, timeout.inWholeSeconds.toInt())
73 | } finally {
74 | Disposer.dispose(disposable)
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/github/oxc/project/oxcintellijplugin/oxfmt/OxfmtPackage.kt:
--------------------------------------------------------------------------------
1 | package com.github.oxc.project.oxcintellijplugin.oxfmt
2 |
3 | import com.github.oxc.project.oxcintellijplugin.ConfigurationMode
4 | import com.github.oxc.project.oxcintellijplugin.ProcessCommandParameter
5 | import com.github.oxc.project.oxcintellijplugin.oxfmt.settings.OxfmtSettings
6 | import com.intellij.javascript.nodejs.interpreter.NodeJsInterpreterManager
7 | import com.intellij.javascript.nodejs.util.NodePackage
8 | import com.intellij.javascript.nodejs.util.NodePackageDescriptor
9 | import com.intellij.openapi.project.Project
10 | import com.intellij.openapi.vfs.VirtualFile
11 | import java.nio.file.Paths
12 |
13 | class OxfmtPackage(private val project: Project) {
14 |
15 | private val packageName = "oxfmt"
16 | private val packageDescription = NodePackageDescriptor(packageName)
17 |
18 | fun getPackage(virtualFile: VirtualFile?): NodePackage? {
19 | if (virtualFile != null) {
20 | val available = packageDescription.listAvailable(
21 | project,
22 | NodeJsInterpreterManager.getInstance(project).interpreter,
23 | virtualFile,
24 | false,
25 | true
26 | )
27 | if (available.isNotEmpty()) {
28 | return available[0]
29 | }
30 | }
31 |
32 | val pkg = packageDescription.findUnambiguousDependencyPackage(project)
33 | ?: NodePackage.findDefaultPackage(
34 | project,
35 | packageName,
36 | NodeJsInterpreterManager.getInstance(project).interpreter
37 | )
38 |
39 | return pkg
40 | }
41 |
42 | fun configPath(): String? {
43 | val settings = OxfmtSettings.getInstance(project)
44 | val configurationMode = settings.configurationMode
45 | return when (configurationMode) {
46 | ConfigurationMode.DISABLED -> null
47 | ConfigurationMode.AUTOMATIC -> null
48 | ConfigurationMode.MANUAL -> settings.configPath
49 | }
50 | }
51 |
52 | fun binaryPath(
53 | virtualFile: VirtualFile,
54 | ): String? {
55 | val settings = OxfmtSettings.getInstance(project)
56 | val configurationMode = settings.configurationMode
57 |
58 | return when (configurationMode) {
59 | ConfigurationMode.DISABLED -> null
60 | ConfigurationMode.AUTOMATIC -> findOxfmtExecutable(virtualFile)
61 | ConfigurationMode.MANUAL -> settings.binaryPath.ifBlank {
62 | findOxfmtExecutable(virtualFile)
63 | }
64 | }
65 | }
66 |
67 | fun binaryParameters(virtualFile: VirtualFile): List {
68 | return findOxfmtParameters(virtualFile)
69 | }
70 |
71 | fun isEnabled(): Boolean {
72 | val settings = OxfmtSettings.getInstance(project)
73 | return settings.configurationMode != ConfigurationMode.DISABLED
74 | }
75 |
76 | private fun findOxfmtExecutable(virtualFile: VirtualFile): String? {
77 | val oxfmtPackage = getPackage(virtualFile) ?: return null
78 | val path = oxfmtPackage.getAbsolutePackagePathToRequire(project)
79 | if (path != null) {
80 | return Paths.get(path, "bin/oxfmt").toString()
81 | }
82 |
83 | return null
84 | }
85 |
86 | private fun findOxfmtParameters(virtualFile: VirtualFile): List {
87 | return listOf(ProcessCommandParameter.Value("--lsp"))
88 | }
89 |
90 | companion object {
91 |
92 | const val CONFIG_NAME = ".oxfmtrc"
93 | val CONFIG_VALID_EXTENSIONS = listOf("json", "jsonc")
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/github/oxc/project/oxcintellijplugin/oxlint/services/OxlintServerService.kt:
--------------------------------------------------------------------------------
1 | package com.github.oxc.project.oxcintellijplugin.oxlint.services
2 |
3 | import com.github.oxc.project.oxcintellijplugin.NOTIFICATION_GROUP
4 | import com.github.oxc.project.oxcintellijplugin.oxlint.OxlintBundle
5 | import com.github.oxc.project.oxcintellijplugin.oxlint.lsp.OxlintLspServerSupportProvider
6 | import com.intellij.notification.NotificationGroupManager
7 | import com.intellij.notification.NotificationType
8 | import com.intellij.openapi.command.WriteCommandAction
9 | import com.intellij.openapi.components.Service
10 | import com.intellij.openapi.editor.Document
11 | import com.intellij.openapi.fileEditor.FileDocumentManager
12 | import com.intellij.openapi.project.Project
13 | import com.intellij.openapi.vfs.VirtualFile
14 | import com.intellij.platform.lsp.api.LspServerManager
15 | import com.intellij.platform.lsp.api.customization.LspIntentionAction
16 | import com.intellij.platform.lsp.util.getLsp4jRange
17 | import org.eclipse.lsp4j.CodeActionContext
18 | import org.eclipse.lsp4j.CodeActionParams
19 | import org.eclipse.lsp4j.CodeActionTriggerKind
20 |
21 | @Service(Service.Level.PROJECT)
22 | class OxlintServerService(private val project: Project) {
23 | private val groupId = "Oxc"
24 |
25 | companion object {
26 | fun getInstance(project: Project): OxlintServerService = project.getService(OxlintServerService::class.java)
27 | }
28 |
29 | private fun getServer(file: VirtualFile) =
30 | LspServerManager.getInstance(project).getServersForProvider(OxlintLspServerSupportProvider::class.java)
31 | .firstOrNull { server -> server.descriptor.isSupportedFile(file) }
32 |
33 | suspend fun fixAll(document: Document) {
34 | val manager = FileDocumentManager.getInstance()
35 | val file = manager.getFile(document) ?: return
36 |
37 | fixAll(file, document)
38 | }
39 |
40 | suspend fun fixAll(file: VirtualFile, document: Document) {
41 | val server = getServer(file) ?: return
42 |
43 | val commandName = OxlintBundle.message("oxlint.run.quickfix")
44 |
45 | val codeActionParams = CodeActionParams(server.getDocumentIdentifier(file),
46 | getLsp4jRange(document, 0, document.textLength),
47 | CodeActionContext().apply {
48 | diagnostics = emptyList()
49 | only = listOf("source.fixAll.oxc")
50 | triggerKind = CodeActionTriggerKind.Automatic
51 | })
52 |
53 | val codeActionResults = server.sendRequest { it.textDocumentService.codeAction(codeActionParams) }
54 |
55 | WriteCommandAction.runWriteCommandAction(project, commandName, groupId, {
56 | codeActionResults?.forEach {
57 | // Only apply preferred actions which contain real fixes.
58 | // non-preferred options contain fixes such as disable-next-line.
59 | if (it.isRight && it.right.isPreferred) {
60 | val action = LspIntentionAction(server, it.right)
61 | if (action.isAvailable()) {
62 | action.invoke(null)
63 | }
64 | }
65 | }
66 | })
67 | }
68 |
69 | fun restartServer() {
70 | LspServerManager.getInstance(project).stopAndRestartIfNeeded(OxlintLspServerSupportProvider::class.java)
71 | }
72 |
73 | fun stopServer() {
74 | LspServerManager.getInstance(project).stopServers(OxlintLspServerSupportProvider::class.java)
75 | }
76 |
77 | fun notifyRestart() {
78 | NotificationGroupManager.getInstance()
79 | .getNotificationGroup(NOTIFICATION_GROUP)
80 | .createNotification(
81 | OxlintBundle.message("oxlint.language.server.restarted"),
82 | "",
83 | NotificationType.INFORMATION
84 | )
85 | .notify(project)
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | # GitHub Actions Workflow created for handling the release process based on the draft release prepared with the Build workflow.
2 | # Running the publishPlugin task requires all following secrets to be provided: PUBLISH_TOKEN, PRIVATE_KEY, PRIVATE_KEY_PASSWORD, CERTIFICATE_CHAIN.
3 | # See https://plugins.jetbrains.com/docs/intellij/plugin-signing.html for more information.
4 |
5 | permissions: {}
6 |
7 | name: Release
8 | on:
9 | release:
10 | types: [prereleased, released]
11 |
12 | jobs:
13 |
14 | # Prepare and publish the plugin to JetBrains Marketplace repository
15 | release:
16 | name: Publish Plugin
17 | runs-on: ubuntu-latest
18 | permissions:
19 | contents: write
20 | pull-requests: write
21 | steps:
22 |
23 | # Check out current repository
24 | - name: Fetch Sources
25 | uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
26 | with:
27 | ref: ${{ github.event.release.tag_name }}
28 |
29 | # Set up Java environment for the next steps
30 | - name: Setup Java
31 | uses: actions/setup-java@f2beeb24e141e01a676f977032f5a29d81c9e27e # v5.1.0
32 | with:
33 | distribution: zulu
34 | java-version: 21
35 |
36 | # Setup Gradle
37 | - name: Setup Gradle
38 | uses: gradle/actions/setup-gradle@4d9f0ba0025fe599b4ebab900eb7f3a1d93ef4c2 # v5.0.0
39 | with:
40 | cache-read-only: ${{ github.ref != 'refs/heads/main' }}
41 |
42 | # Set environment variables
43 | - name: Export Properties
44 | id: properties
45 | shell: bash
46 | run: |
47 | CHANGELOG="$(cat << 'EOM' | sed -e 's/^[[:space:]]*$//g' -e '/./,$!d'
48 | ${{ github.event.release.body }}
49 | EOM
50 | )"
51 |
52 | echo "changelog<> $GITHUB_OUTPUT
53 | echo "$CHANGELOG" >> $GITHUB_OUTPUT
54 | echo "EOF" >> $GITHUB_OUTPUT
55 |
56 | # Update Unreleased section with the current release note
57 | - name: Patch Changelog
58 | if: ${{ steps.properties.outputs.changelog != '' }}
59 | env:
60 | CHANGELOG: ${{ steps.properties.outputs.changelog }}
61 | run: |
62 | ./gradlew patchChangelog --release-note="$CHANGELOG"
63 |
64 | # Publish the plugin to JetBrains Marketplace
65 | - name: Publish Plugin
66 | env:
67 | PUBLISH_TOKEN: ${{ secrets.PUBLISH_TOKEN }}
68 | CERTIFICATE_CHAIN: ${{ secrets.CERTIFICATE_CHAIN }}
69 | PRIVATE_KEY: ${{ secrets.PRIVATE_KEY }}
70 | PRIVATE_KEY_PASSWORD: ${{ secrets.PRIVATE_KEY_PASSWORD }}
71 | run: ./gradlew publishPlugin
72 |
73 | # Upload artifact as a release asset
74 | - name: Upload Release Asset
75 | env:
76 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
77 | run: gh release upload ${{ github.event.release.tag_name }} ./build/distributions/*
78 |
79 | # Create a pull request
80 | - name: Create Pull Request
81 | if: ${{ steps.properties.outputs.changelog != '' }}
82 | env:
83 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
84 | run: |
85 | VERSION="${{ github.event.release.tag_name }}"
86 | BRANCH="changelog-update-$VERSION"
87 | LABEL="release changelog"
88 |
89 | git config user.email "action@github.com"
90 | git config user.name "GitHub Action"
91 |
92 | git checkout -b $BRANCH
93 | git commit -am "Changelog update - $VERSION"
94 | git push --set-upstream origin $BRANCH
95 |
96 | gh label create "$LABEL" \
97 | --description "Pull requests with release changelog update" \
98 | --force \
99 | || true
100 |
101 | gh pr create \
102 | --title "Changelog update - \`$VERSION\`" \
103 | --body "Current pull request contains patched \`CHANGELOG.md\` file for the \`$VERSION\` version." \
104 | --label "$LABEL" \
105 | --head $BRANCH
106 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/github/oxc/project/oxcintellijplugin/oxfmt/lsp/OxfmtLspServerDescriptor.kt:
--------------------------------------------------------------------------------
1 | package com.github.oxc.project.oxcintellijplugin.oxfmt.lsp
2 |
3 | import com.github.oxc.project.oxcintellijplugin.OxcTargetRun
4 | import com.github.oxc.project.oxcintellijplugin.OxcTargetRunBuilder
5 | import com.github.oxc.project.oxcintellijplugin.ProcessCommandParameter
6 | import com.github.oxc.project.oxcintellijplugin.oxfmt.OxfmtPackage
7 | import com.github.oxc.project.oxcintellijplugin.oxfmt.settings.OxfmtSettings
8 | import com.intellij.execution.configurations.GeneralCommandLine
9 | import com.intellij.execution.process.OSProcessHandler
10 | import com.intellij.openapi.diagnostic.thisLogger
11 | import com.intellij.openapi.project.Project
12 | import com.intellij.openapi.vfs.VirtualFile
13 | import com.intellij.platform.lsp.api.LspServerDescriptor
14 | import org.eclipse.lsp4j.ClientCapabilities
15 | import org.eclipse.lsp4j.ConfigurationItem
16 | import org.eclipse.lsp4j.InitializeParams
17 |
18 | class OxfmtLspServerDescriptor(
19 | project: Project,
20 | root: VirtualFile,
21 | executable: String,
22 | executableParameters: List,
23 | ) : LspServerDescriptor(project, "Oxfmt", root) {
24 |
25 | private val targetRun: OxcTargetRun = run {
26 | val settings = OxfmtSettings.getInstance(project)
27 | val builder = OxcTargetRunBuilder(project).getBuilder(settings.configurationMode,
28 | executable).setWorkingDirectory(root.path).addParameters(executableParameters)
29 |
30 | builder.build()
31 | }
32 |
33 | override fun isSupportedFile(file: VirtualFile): Boolean {
34 | thisLogger().debug("file.path ${file.path}")
35 | return OxfmtSettings.getInstance(project).fileSupported(file) && roots.any { root ->
36 | file.toNioPath().startsWith(root.toNioPath())
37 | }
38 | }
39 |
40 | override fun createCommandLine(): GeneralCommandLine {
41 | throw RuntimeException(
42 | "Not expected to be called because startServerProcess() is overridden")
43 | }
44 |
45 | override fun startServerProcess(): OSProcessHandler = targetRun.startProcess()
46 |
47 | override fun getFilePath(file: VirtualFile): String = targetRun.toTargetPath(file.path)
48 |
49 | override fun findLocalFileByPath(path: String): VirtualFile? =
50 | super.findLocalFileByPath(targetRun.toLocalPath(path))
51 |
52 | override fun createInitializationOptions(): Any {
53 | val initializationOptions = roots.map {
54 | return@map mapOf("workspaceUri" to it.toNioPath().toUri().toString(),
55 | "options" to createWorkspaceConfig(it))
56 | }
57 | thisLogger().debug("Initialization options: $initializationOptions")
58 | return initializationOptions
59 | }
60 |
61 | override fun createInitializeParams(): InitializeParams {
62 | val params = super.createInitializeParams()
63 | thisLogger().debug("Initialization params: $params")
64 | return params
65 | }
66 |
67 | override fun getWorkspaceConfiguration(item: ConfigurationItem): Any? {
68 | val myRoot = roots.find {
69 | return@find it.toNioPath().toUri().toString() == item.scopeUri
70 | } ?: return null
71 | return createWorkspaceConfig(myRoot)
72 | }
73 |
74 | override val clientCapabilities: ClientCapabilities
75 | get() {
76 | thisLogger().debug("Client Capabilities: ${super.clientCapabilities}")
77 | return super.clientCapabilities.apply {
78 | workspace.apply {
79 | configuration = true
80 | }
81 | }
82 | }
83 |
84 | override val lspGoToDefinitionSupport = false
85 |
86 | override val lspCompletionSupport = null
87 |
88 | override val lspFormattingSupport = OxfmtLspFormattingSupport(project)
89 |
90 | override val lspHoverSupport = false
91 |
92 | override val lspDiagnosticsSupport = null
93 |
94 | private fun createWorkspaceConfig(workspace: VirtualFile): Map {
95 | val oxfmtPackage = OxfmtPackage(project)
96 |
97 | return mapOf(
98 | "configPath" to null,
99 | "flags" to emptyMap(),
100 | "fmt.experimental" to true,
101 | "fmt.configPath" to oxfmtPackage.configPath(),
102 | "run" to "onSave",
103 | "typeAware" to false,
104 | "unusedDisableDirectives" to false,
105 | )
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/github/oxc/project/oxcintellijplugin/OxcTargetRun.kt:
--------------------------------------------------------------------------------
1 | package com.github.oxc.project.oxcintellijplugin
2 |
3 | import com.github.oxc.project.oxcintellijplugin.oxlint.OxlintBundle
4 | import com.intellij.execution.ExecutionException
5 | import com.intellij.execution.configurations.GeneralCommandLine
6 | import com.intellij.execution.process.CapturingProcessHandler
7 | import com.intellij.execution.process.OSProcessHandler
8 | import com.intellij.execution.wsl.WSLDistribution
9 | import com.intellij.execution.wsl.WslPath
10 | import com.intellij.javascript.nodejs.execution.NodeTargetRun
11 | import com.intellij.javascript.nodejs.interpreter.NodeJsInterpreterManager
12 | import com.intellij.javascript.nodejs.interpreter.local.NodeJsLocalInterpreter
13 | import com.intellij.javascript.nodejs.interpreter.wsl.WslNodeInterpreter
14 | import com.intellij.lang.javascript.JavaScriptBundle
15 | import com.intellij.openapi.diagnostic.Logger
16 | import com.intellij.openapi.progress.EmptyProgressIndicator
17 | import com.intellij.openapi.progress.ProgressManager
18 | import com.intellij.openapi.project.Project
19 | import com.intellij.openapi.util.Computable
20 | import com.intellij.util.io.BaseOutputReader
21 | import java.io.File
22 | import kotlin.io.path.Path
23 |
24 |
25 | fun wrapStartProcess(processCreator: () -> OSProcessHandler): OSProcessHandler =
26 | ProgressManager.getInstance().runProcess(Computable(processCreator), EmptyProgressIndicator())
27 |
28 | sealed interface OxcTargetRun {
29 | fun startProcess(): OSProcessHandler
30 | fun toTargetPath(path: String): String
31 | fun toLocalPath(path: String): String
32 |
33 | class Node(private val run: NodeTargetRun) : OxcTargetRun {
34 | override fun startProcess(): OSProcessHandler =
35 | wrapStartProcess {
36 | if (!run.envData.envs.contains("RUST_LOG")) {
37 | val logger = Logger.getInstance("#com.github.oxc.project.oxcintellijplugin")
38 | val level = if (logger.isTraceEnabled) "TRACE" else if (logger.isDebugEnabled) "DEBUG" else "INFO"
39 | run.envData = run.envData.with(mapOf("RUST_LOG" to level))
40 | }
41 | run.startProcessEx().processHandler
42 | }
43 |
44 | override fun toTargetPath(path: String) = run.convertLocalPathToTargetPath(path)
45 | override fun toLocalPath(path: String) = run.convertTargetPathToLocalPath(path)
46 | }
47 |
48 | class General(
49 | private val command: GeneralCommandLine,
50 | private val wslDistribution: WSLDistribution? = null,
51 | ) : OxcTargetRun {
52 | override fun startProcess(): OSProcessHandler =
53 | wrapStartProcess {
54 | object : CapturingProcessHandler(command) {
55 | override fun readerOptions(): BaseOutputReader.Options {
56 | return object : BaseOutputReader.Options() {
57 | override fun splitToLines(): Boolean = false
58 | }
59 | }
60 | }
61 | }
62 |
63 | override fun toTargetPath(path: String) = wslDistribution?.getWslPath(Path(path)) ?: path
64 | override fun toLocalPath(path: String) = wslDistribution?.getWindowsPath(path) ?: path
65 | }
66 | }
67 |
68 | class OxcTargetRunBuilder(val project: Project) {
69 | fun getBuilder(
70 | configMode: ConfigurationMode,
71 | executable: String,
72 | ): ProcessCommandBuilder {
73 | if (executable.isEmpty()) {
74 | throw ExecutionException(OxlintBundle.message("oxlint.language.server.not.found"))
75 | }
76 |
77 | val wslPath = WslPath.parseWindowsUncPath(executable)
78 |
79 | // For WSL paths, always use NodeProcessCommandBuilder as it handles WSL correctly
80 | val isNodeJs = if (wslPath != null) {
81 | true
82 | } else {
83 | runCatching {
84 | File(executable).useLines { it.firstOrNull() }
85 | ?.startsWith("#!/usr/bin/env node")
86 | }.getOrNull() == true
87 | }
88 |
89 | val builder: ProcessCommandBuilder = if (configMode == ConfigurationMode.MANUAL && !isNodeJs) {
90 | GeneralProcessCommandBuilder()
91 | } else {
92 | val interpreter = NodeJsInterpreterManager.getInstance(project).interpreter
93 | if (interpreter !is NodeJsLocalInterpreter && interpreter !is WslNodeInterpreter) {
94 | throw ExecutionException(JavaScriptBundle.message("lsp.interpreter.error"))
95 | }
96 | NodeProcessCommandBuilder(project, interpreter)
97 | }
98 |
99 | return builder.setExecutable(executable).setCharset(Charsets.UTF_8)
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/github/oxc/project/oxcintellijplugin/oxlint/lsp/OxlintLspServerDescriptor.kt:
--------------------------------------------------------------------------------
1 | package com.github.oxc.project.oxcintellijplugin.oxlint.lsp
2 |
3 | import com.github.oxc.project.oxcintellijplugin.OxcTargetRun
4 | import com.github.oxc.project.oxcintellijplugin.OxcTargetRunBuilder
5 | import com.github.oxc.project.oxcintellijplugin.ProcessCommandParameter
6 | import com.github.oxc.project.oxcintellijplugin.oxlint.OxlintPackage
7 | import com.github.oxc.project.oxcintellijplugin.oxlint.settings.OxlintSettings
8 | import com.intellij.execution.configurations.GeneralCommandLine
9 | import com.intellij.execution.process.OSProcessHandler
10 | import com.intellij.openapi.diagnostic.thisLogger
11 | import com.intellij.openapi.project.Project
12 | import com.intellij.openapi.vfs.VirtualFile
13 | import com.intellij.platform.lsp.api.LspServerDescriptor
14 | import com.intellij.platform.lsp.api.customization.LspDiagnosticsSupport
15 | import org.eclipse.lsp4j.ClientCapabilities
16 | import org.eclipse.lsp4j.ConfigurationItem
17 | import org.eclipse.lsp4j.InitializeParams
18 |
19 | class OxlintLspServerDescriptor(
20 | project: Project,
21 | root: VirtualFile,
22 | executable: String,
23 | executableParameters: List,
24 | ) : LspServerDescriptor(project, "Oxlint", root) {
25 | private val targetRun: OxcTargetRun = run {
26 | val oxlintSettings = OxlintSettings.getInstance(project)
27 | val builder = OxcTargetRunBuilder(project).getBuilder(oxlintSettings.configurationMode, executable).setWorkingDirectory(root.path).addParameters(executableParameters)
28 |
29 | builder.build()
30 | }
31 |
32 | override fun isSupportedFile(file: VirtualFile): Boolean {
33 | thisLogger().debug("file.path ${file.path}")
34 | return OxlintSettings.getInstance(project).fileSupported(file) && roots.any { root ->
35 | file.toNioPath().startsWith(root.toNioPath())
36 | }
37 | }
38 |
39 | override fun createCommandLine(): GeneralCommandLine {
40 | throw RuntimeException("Not expected to be called because startServerProcess() is overridden")
41 | }
42 |
43 | override fun startServerProcess(): OSProcessHandler =
44 | targetRun.startProcess()
45 |
46 | override fun getFilePath(file: VirtualFile): String =
47 | targetRun.toTargetPath(file.path)
48 |
49 | override fun findLocalFileByPath(path: String): VirtualFile? =
50 | super.findLocalFileByPath(targetRun.toLocalPath(path))
51 |
52 | override fun createInitializationOptions(): Any {
53 | val initializationOptions = roots.map {
54 | return@map mapOf(
55 | "workspaceUri" to it.toNioPath().toUri().toString(),
56 | "options" to createWorkspaceConfig(it)
57 | )
58 | }
59 | thisLogger().debug("Initialization options: $initializationOptions")
60 | return initializationOptions
61 | }
62 |
63 | override fun createInitializeParams(): InitializeParams {
64 | val params = super.createInitializeParams()
65 | thisLogger().debug("Initialization params: $params")
66 | return params
67 | }
68 |
69 | override fun getWorkspaceConfiguration(item: ConfigurationItem): Any? {
70 | val myRoot = roots.find {
71 | return@find it.toNioPath().toUri().toString() == item.scopeUri
72 | } ?: return null
73 | return createWorkspaceConfig(myRoot)
74 | }
75 |
76 | override val clientCapabilities: ClientCapabilities
77 | get() {
78 | thisLogger().debug("Client Capabilities: ${super.clientCapabilities}")
79 | return super.clientCapabilities.apply {
80 | workspace.apply {
81 | configuration = true
82 | }
83 | }
84 | }
85 |
86 | override val lspGoToDefinitionSupport = false
87 |
88 | override val lspCompletionSupport = null
89 |
90 | override val lspFormattingSupport = null
91 |
92 | override val lspHoverSupport = false
93 |
94 | override val lspDiagnosticsSupport: LspDiagnosticsSupport = OxlintLspDiagnosticsSupport()
95 |
96 | private fun createWorkspaceConfig(workspace: VirtualFile): Map {
97 | val oxlintPackage = OxlintPackage(project)
98 | val settings = OxlintSettings.getInstance(project)
99 |
100 | return mapOf(
101 | "configPath" to oxlintPackage.configPath(),
102 | "flags" to mapOf(
103 | "disable_nested_config" to settings.disableNestedConfig.toString(),
104 | "fix_kind" to settings.fixKind.toLspValue(),
105 | ),
106 | "run" to settings.state.runTrigger.toLspValue(),
107 | "typeAware" to settings.typeAware,
108 | "unusedDisableDirectives" to settings.state.unusedDisableDirectives.toLspValue(),
109 | )
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/github/oxc/project/oxcintellijplugin/oxlint/OxlintPackage.kt:
--------------------------------------------------------------------------------
1 | package com.github.oxc.project.oxcintellijplugin.oxlint
2 |
3 | import com.github.oxc.project.oxcintellijplugin.ConfigurationMode
4 | import com.github.oxc.project.oxcintellijplugin.ProcessCommandParameter
5 | import com.github.oxc.project.oxcintellijplugin.oxlint.settings.OxlintSettings
6 | import com.intellij.javascript.nodejs.interpreter.NodeJsInterpreterManager
7 | import com.intellij.javascript.nodejs.util.NodePackage
8 | import com.intellij.javascript.nodejs.util.NodePackageDescriptor
9 | import com.intellij.openapi.project.Project
10 | import com.intellij.openapi.vfs.VirtualFile
11 | import com.intellij.util.text.SemVer
12 | import java.nio.file.Paths
13 |
14 | class OxlintPackage(private val project: Project) {
15 | private val packageName = "oxlint"
16 | private val packageDescription = NodePackageDescriptor(packageName)
17 |
18 | fun getPackage(virtualFile: VirtualFile?): NodePackage? {
19 | if (virtualFile != null) {
20 | val available = packageDescription.listAvailable(
21 | project,
22 | NodeJsInterpreterManager.getInstance(project).interpreter,
23 | virtualFile,
24 | false,
25 | true
26 | )
27 | if (available.isNotEmpty()) {
28 | return available[0]
29 | }
30 | }
31 |
32 | val pkg = packageDescription.findUnambiguousDependencyPackage(project) ?: NodePackage.findDefaultPackage(
33 | project,
34 | packageName,
35 | NodeJsInterpreterManager.getInstance(project).interpreter
36 | )
37 |
38 | return pkg
39 | }
40 |
41 | fun configPath(): String? {
42 | val settings = OxlintSettings.getInstance(project)
43 | val configurationMode = settings.configurationMode
44 | return when (configurationMode) {
45 | ConfigurationMode.DISABLED -> null
46 | ConfigurationMode.AUTOMATIC -> null
47 | ConfigurationMode.MANUAL -> settings.configPath
48 | }
49 | }
50 |
51 | fun binaryPath(
52 | virtualFile: VirtualFile,
53 | ): String? {
54 | val settings = OxlintSettings.getInstance(project)
55 | val configurationMode = settings.configurationMode
56 |
57 | return when (configurationMode) {
58 | ConfigurationMode.DISABLED -> null
59 | ConfigurationMode.AUTOMATIC -> findOxlintExecutable(virtualFile)
60 | ConfigurationMode.MANUAL -> settings.binaryPath.ifBlank { findOxlintExecutable(virtualFile) }
61 | }
62 | }
63 |
64 | fun binaryParameters(virtualFile: VirtualFile): List {
65 | val settings = OxlintSettings.getInstance(project)
66 | val configurationMode = settings.configurationMode
67 |
68 | return when (configurationMode) {
69 | ConfigurationMode.DISABLED -> emptyList()
70 | ConfigurationMode.AUTOMATIC -> {
71 | findOxlintParameters(virtualFile)
72 | }
73 | ConfigurationMode.MANUAL -> {
74 | if (settings.binaryPath.isBlank()) {
75 | findOxlintParameters(virtualFile)
76 | } else {
77 | settings.binaryParameters.map { ProcessCommandParameter.Value(it) }
78 | }
79 | }
80 | }
81 | }
82 |
83 | fun isEnabled(): Boolean {
84 | val settings = OxlintSettings.getInstance(project)
85 | return settings.configurationMode != ConfigurationMode.DISABLED
86 | }
87 |
88 | private fun findOxlintExecutable(virtualFile: VirtualFile): String? {
89 | val oxlintPackage = getPackage(virtualFile) ?: return null
90 | val path = oxlintPackage.getAbsolutePackagePathToRequire(project)
91 | if (path != null) {
92 | val version = oxlintPackage.getVersion(project)
93 |
94 | return if (version?.isGreaterOrEqualThan(OXLINT_FIRST_LSP_VERSION) == true) {
95 | Paths.get(path, "bin/oxlint").toString()
96 | } else {
97 | Paths.get(path, "bin/oxc_language_server").toString()
98 | }
99 | }
100 |
101 | return null
102 | }
103 |
104 | private fun findOxlintParameters(virtualFile: VirtualFile): List {
105 | val oxlintPackage = getPackage(virtualFile) ?: return emptyList()
106 | val version = oxlintPackage.getVersion(project)
107 |
108 | return if (version?.isGreaterOrEqualThan(OXLINT_FIRST_LSP_VERSION) == true) {
109 | listOf(ProcessCommandParameter.Value("--lsp"))
110 | } else {
111 | emptyList()
112 | }
113 | }
114 |
115 | companion object {
116 | const val CONFIG_NAME = ".oxlintrc"
117 | val OXLINT_FIRST_LSP_VERSION = SemVer("1.29.0", 1, 29, 0)
118 | val configValidExtensions = listOf("json")
119 | }
120 | }
121 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/github/oxc/project/oxcintellijplugin/oxlint/actions/OxlintFixAllAction.kt:
--------------------------------------------------------------------------------
1 | package com.github.oxc.project.oxcintellijplugin.oxlint.actions
2 |
3 | import com.github.oxc.project.oxcintellijplugin.NOTIFICATION_GROUP
4 | import com.github.oxc.project.oxcintellijplugin.OxcIcons
5 | import com.github.oxc.project.oxcintellijplugin.oxlint.OxlintBundle
6 | import com.github.oxc.project.oxcintellijplugin.oxlint.services.OxlintServerService
7 | import com.github.oxc.project.oxcintellijplugin.oxlint.settings.OxlintConfigurable
8 | import com.github.oxc.project.oxcintellijplugin.oxlint.settings.OxlintSettings
9 | import com.intellij.notification.NotificationAction
10 | import com.intellij.notification.NotificationGroupManager
11 | import com.intellij.notification.NotificationType
12 | import com.intellij.openapi.actionSystem.ActionUpdateThread
13 | import com.intellij.openapi.actionSystem.AnAction
14 | import com.intellij.openapi.actionSystem.AnActionEvent
15 | import com.intellij.openapi.actionSystem.CommonDataKeys
16 | import com.intellij.openapi.editor.Document
17 | import com.intellij.openapi.editor.EditorFactory
18 | import com.intellij.openapi.fileEditor.FileDocumentManager
19 | import com.intellij.openapi.options.ShowSettingsUtil
20 | import com.intellij.openapi.project.DumbAware
21 | import com.intellij.openapi.vfs.VirtualFile
22 | import com.intellij.openapi.vfs.readText
23 | import com.intellij.platform.ide.progress.runWithModalProgressBlocking
24 | import java.io.IOException
25 | import kotlinx.coroutines.withTimeout
26 |
27 | class OxlintFixAllAction : AnAction(), DumbAware {
28 | init {
29 | templatePresentation.icon = OxcIcons.OxcRound
30 | }
31 |
32 | override fun actionPerformed(event: AnActionEvent) {
33 | val project = event.project ?: return
34 | val (virtualFile, document) = event.getFileAndDocument() ?: return
35 |
36 | val notificationGroup = NotificationGroupManager.getInstance().getNotificationGroup(NOTIFICATION_GROUP)
37 |
38 | val settings = OxlintSettings.getInstance(project)
39 |
40 | if (!settings.fileSupported(virtualFile)) {
41 | notificationGroup.createNotification(title = OxlintBundle.message("oxlint.file.not.supported.title"),
42 | content = OxlintBundle.message("oxlint.file.not.supported.description", virtualFile.name),
43 | type = NotificationType.WARNING)
44 | .addAction(NotificationAction.createSimple(OxlintBundle.message("oxlint.configure.extensions.link")) {
45 | ShowSettingsUtil.getInstance().showSettingsDialog(project, OxlintConfigurable::class.java)
46 | }).notify(project)
47 | return
48 | }
49 |
50 | runWithModalProgressBlocking(project,
51 | OxlintBundle.message("oxlint.run.fix.all")) {
52 | try {
53 | withTimeout(5_000) {
54 | OxlintServerService.getInstance(project).fixAll(virtualFile, document)
55 | }
56 | notificationGroup.createNotification(title = OxlintBundle.message("oxlint.fix.all.success.label"),
57 | content = OxlintBundle.message("oxlint.fix.all.success.description"),
58 | type = NotificationType.INFORMATION).notify(project)
59 | } catch (e: Exception) {
60 | notificationGroup.createNotification(title = OxlintBundle.message("oxlint.fix.all.failure.label"),
61 | content = OxlintBundle.message("oxlint.fix.all.failure.description", e.message.toString()),
62 | type = NotificationType.ERROR).notify(project)
63 | }
64 | }
65 | }
66 |
67 | override fun update(e: AnActionEvent) {
68 | val project = e.project ?: return
69 | val settings = OxlintSettings.getInstance(project)
70 |
71 | val file = e.getData(CommonDataKeys.VIRTUAL_FILE)
72 | val enabled = file != null && settings.fileSupported(file) && settings.isEnabled()
73 |
74 | if (e.isFromContextMenu) {
75 | e.presentation.isVisible = enabled
76 | }
77 | e.presentation.isEnabled = enabled
78 | }
79 |
80 | override fun getActionUpdateThread(): ActionUpdateThread {
81 | return ActionUpdateThread.BGT
82 | }
83 |
84 | private fun AnActionEvent.getFileAndDocument(): Pair? {
85 | getData(CommonDataKeys.EDITOR)?.let {
86 | val document = it.document
87 |
88 | val manager = FileDocumentManager.getInstance()
89 | val file = manager.getFile(document) ?: return null
90 |
91 | return file to document
92 | }
93 |
94 | val file = getData(CommonDataKeys.VIRTUAL_FILE) ?: return null
95 | val text = try {
96 | file.readText()
97 | } catch (_: IOException) {
98 | return null
99 | }
100 |
101 | val document = EditorFactory.getInstance().createDocument(text)
102 |
103 | return file to document
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/src/main/resources/META-INF/plugin.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | com.github.oxc.project.oxcintellijplugin
4 | Oxc
5 | Oxc
6 |
7 | com.intellij.modules.platform
8 | JavaScript
9 |
10 | messages.OxfmtBundle
11 | messages.OxlintBundle
12 |
13 |
14 |
15 |
16 |
17 |
19 |
21 |
22 |
30 |
38 |
39 |
44 |
45 |
50 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
69 |
70 |
71 |
72 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
88 |
92 |
93 |
94 |
95 |
97 |
99 |
100 |
101 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/github/oxc/project/oxcintellijplugin/oxlint/settings/OxlintSettings.kt:
--------------------------------------------------------------------------------
1 | package com.github.oxc.project.oxcintellijplugin.oxlint.settings
2 |
3 | import com.github.oxc.project.oxcintellijplugin.ConfigurationMode
4 | import com.github.oxc.project.oxcintellijplugin.oxlint.OxlintFixKind
5 | import com.github.oxc.project.oxcintellijplugin.oxlint.settings.OxlintSettingsState.Companion.DEFAULT_EXTENSION_LIST
6 | import com.intellij.openapi.components.Service
7 | import com.intellij.openapi.components.SettingsCategory
8 | import com.intellij.openapi.components.SimplePersistentStateComponent
9 | import com.intellij.openapi.components.State
10 | import com.intellij.openapi.components.Storage
11 | import com.intellij.openapi.components.service
12 | import com.intellij.openapi.diagnostic.thisLogger
13 | import com.intellij.openapi.project.Project
14 | import com.intellij.openapi.vfs.VirtualFile
15 | import java.io.File
16 |
17 | @Service(Service.Level.PROJECT)
18 | @State(name = "OxcSettings", storages = [Storage("OxcSettings.xml")],
19 | category = SettingsCategory.TOOLS)
20 | class OxlintSettings(private val project: Project) :
21 | SimplePersistentStateComponent(OxlintSettingsState()) {
22 |
23 | var configurationMode: ConfigurationMode
24 | get() = state.configurationMode
25 | set(value) {
26 | state.configurationMode = value
27 | }
28 |
29 | var supportedExtensions: MutableList
30 | get() = state.supportedExtensions.takeIf { it.isNotEmpty() } ?: DEFAULT_EXTENSION_LIST.toMutableList()
31 | set(value) {
32 | state.supportedExtensions.clear()
33 | state.supportedExtensions.addAll(value)
34 | }
35 |
36 | var binaryPath: String
37 | get() = state.binaryPath ?: ""
38 | set(value) {
39 | state.binaryPath = value
40 | }
41 |
42 | var binaryParameters: MutableList
43 | get() = state.binaryParameters
44 | set(value) {
45 | state.binaryParameters.clear()
46 | state.binaryParameters.addAll(value)
47 | }
48 |
49 | var configPath: String
50 | get() = state.configPath ?: ""
51 | set(value) {
52 | val file = File(value)
53 | if (file.isFile) {
54 | state.configPath = file.path
55 | return
56 | }
57 | state.configPath = value
58 | }
59 |
60 | var disableNestedConfig: Boolean
61 | get() {
62 | try {
63 | return flags.getOrDefault(DISABLE_NESTED_CONFIG_KEY, "false").toBoolean()
64 | } catch (exception: Exception) {
65 | thisLogger().warn("Invalid value found for $DISABLE_NESTED_CONFIG_KEY", exception)
66 | return false
67 | }
68 | }
69 | set(value) {
70 | flags[DISABLE_NESTED_CONFIG_KEY] = value.toString()
71 | }
72 |
73 | var fixAllOnSave: Boolean
74 | get() = isEnabled() && state.fixAllOnSave
75 | set(value) {
76 | state.fixAllOnSave = value
77 | }
78 |
79 | var fixKind: OxlintFixKind
80 | get() {
81 | try {
82 | return OxlintFixKind.valueOf(
83 | flags.getOrDefault(FIX_KIND_KEY, OxlintFixKind.SAFE_FIX.name).uppercase())
84 | } catch (exception: Exception) {
85 | thisLogger().warn("Invalid value found for $FIX_KIND_KEY", exception)
86 | return OxlintFixKind.SAFE_FIX
87 | }
88 | }
89 | set(value) {
90 | flags[FIX_KIND_KEY] = value.name
91 | }
92 |
93 | /**
94 | * Only intended for legacy use. New config values should be in a separate property.
95 | */
96 | private var flags: MutableMap
97 | get() = state.flags
98 | set(value) {
99 | state.flags = value
100 | }
101 |
102 | var runTrigger
103 | get() = state.runTrigger
104 | set(value) {
105 | state.runTrigger = value
106 | }
107 |
108 | var typeAware
109 | get() = state.typeAware
110 | set(value) {
111 | state.typeAware = value
112 | }
113 |
114 | var unusedDisableDirectivesSeverity
115 | get() = state.unusedDisableDirectives
116 | set(value) {
117 | state.unusedDisableDirectives = value
118 | }
119 |
120 | fun isEnabled(): Boolean {
121 | return configurationMode !== ConfigurationMode.DISABLED
122 | }
123 |
124 | fun fileSupported(file: VirtualFile): Boolean {
125 | val fileExtension = file.extension
126 | return if (fileExtension != null) {
127 | supportedExtensions.contains(".$fileExtension")
128 | } else {
129 | false
130 | }
131 | }
132 |
133 | companion object {
134 |
135 | const val DISABLE_NESTED_CONFIG_KEY = "disable_nested_config";
136 | const val FIX_KIND_KEY = "fix_kind";
137 |
138 | @JvmStatic
139 | fun getInstance(project: Project): OxlintSettings = project.service()
140 | }
141 | }
142 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/github/oxc/project/oxcintellijplugin/oxfmt/actions/OxfmtFixAllAction.kt:
--------------------------------------------------------------------------------
1 | package com.github.oxc.project.oxcintellijplugin.oxfmt.actions
2 |
3 | import com.github.oxc.project.oxcintellijplugin.NOTIFICATION_GROUP
4 | import com.github.oxc.project.oxcintellijplugin.OxcIcons
5 | import com.github.oxc.project.oxcintellijplugin.oxfmt.OxfmtBundle
6 | import com.github.oxc.project.oxcintellijplugin.oxfmt.services.OxfmtServerService
7 | import com.github.oxc.project.oxcintellijplugin.oxfmt.settings.OxfmtConfigurable
8 | import com.github.oxc.project.oxcintellijplugin.oxfmt.settings.OxfmtSettings
9 | import com.intellij.notification.NotificationAction
10 | import com.intellij.notification.NotificationGroupManager
11 | import com.intellij.notification.NotificationType
12 | import com.intellij.openapi.actionSystem.ActionUpdateThread
13 | import com.intellij.openapi.actionSystem.AnAction
14 | import com.intellij.openapi.actionSystem.AnActionEvent
15 | import com.intellij.openapi.actionSystem.CommonDataKeys
16 | import com.intellij.openapi.editor.Document
17 | import com.intellij.openapi.editor.EditorFactory
18 | import com.intellij.openapi.fileEditor.FileDocumentManager
19 | import com.intellij.openapi.options.ShowSettingsUtil
20 | import com.intellij.openapi.project.DumbAware
21 | import com.intellij.openapi.vfs.VirtualFile
22 | import com.intellij.openapi.vfs.readText
23 | import com.intellij.platform.ide.progress.runWithModalProgressBlocking
24 | import java.io.IOException
25 | import kotlinx.coroutines.withTimeout
26 |
27 | class OxfmtFixAllAction : AnAction(), DumbAware {
28 | init {
29 | templatePresentation.icon = OxcIcons.OxcRound
30 | }
31 |
32 | override fun actionPerformed(event: AnActionEvent) {
33 | val project = event.project ?: return
34 | val (virtualFile, document) = event.getFileAndDocument() ?: return
35 |
36 | val notificationGroup = NotificationGroupManager.getInstance().getNotificationGroup(NOTIFICATION_GROUP)
37 |
38 | val settings = OxfmtSettings.getInstance(project)
39 |
40 | if (!settings.fileSupported(virtualFile)) {
41 | notificationGroup.createNotification(
42 | title = OxfmtBundle.message("oxfmt.file.not.supported.title"),
43 | content = OxfmtBundle.message("oxfmt.file.not.supported.description",
44 | virtualFile.name),
45 | type = NotificationType.WARNING)
46 | .addAction(NotificationAction.createSimple(
47 | OxfmtBundle.message("oxfmt.configure.extensions.link")) {
48 | ShowSettingsUtil.getInstance()
49 | .showSettingsDialog(project, OxfmtConfigurable::class.java)
50 | }).notify(project)
51 | return
52 | }
53 |
54 | runWithModalProgressBlocking(project,
55 | OxfmtBundle.message("oxfmt.run.fix.all")) {
56 | try {
57 | withTimeout(5_000) {
58 | OxfmtServerService.getInstance(project).fixAll(virtualFile, document)
59 | }
60 | notificationGroup.createNotification(
61 | title = OxfmtBundle.message("oxfmt.fix.all.success.label"),
62 | content = OxfmtBundle.message("oxfmt.fix.all.success.description"),
63 | type = NotificationType.INFORMATION).notify(project)
64 | } catch (e: Exception) {
65 | notificationGroup.createNotification(
66 | title = OxfmtBundle.message("oxfmt.fix.all.failure.label"),
67 | content = OxfmtBundle.message("oxfmt.fix.all.failure.description",
68 | e.message.toString()),
69 | type = NotificationType.ERROR).notify(project)
70 | }
71 | }
72 | }
73 |
74 | override fun update(e: AnActionEvent) {
75 | val project = e.project ?: return
76 | val settings = OxfmtSettings.getInstance(project)
77 |
78 | val file = e.getData(CommonDataKeys.VIRTUAL_FILE)
79 | val enabled = file != null && settings.fileSupported(file) && settings.isEnabled()
80 |
81 | if (e.isFromContextMenu) {
82 | e.presentation.isVisible = enabled
83 | }
84 | e.presentation.isEnabled = enabled
85 | }
86 |
87 | override fun getActionUpdateThread(): ActionUpdateThread {
88 | return ActionUpdateThread.BGT
89 | }
90 |
91 | private fun AnActionEvent.getFileAndDocument(): Pair? {
92 | getData(CommonDataKeys.EDITOR)?.let {
93 | val document = it.document
94 |
95 | val manager = FileDocumentManager.getInstance()
96 | val file = manager.getFile(document) ?: return null
97 |
98 | return file to document
99 | }
100 |
101 | val file = getData(CommonDataKeys.VIRTUAL_FILE) ?: return null
102 | val text = try {
103 | file.readText()
104 | } catch (_: IOException) {
105 | return null
106 | }
107 |
108 | val document = EditorFactory.getInstance().createDocument(text)
109 |
110 | return file to document
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/sandbox/oxlint/nested-configs/.oxlintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": [
3 | "react",
4 | "unicorn",
5 | "typescript",
6 | "oxc"
7 | ],
8 | "categories": {},
9 | "rules": {
10 | "curly": "deny",
11 | "for-direction": "warn",
12 | "no-async-promise-executor": "warn",
13 | "no-caller": "warn",
14 | "no-class-assign": "warn",
15 | "no-compare-neg-zero": "warn",
16 | "no-cond-assign": "warn",
17 | "no-const-assign": "warn",
18 | "no-constant-binary-expression": "warn",
19 | "no-constant-condition": "warn",
20 | "no-control-regex": "warn",
21 | "no-debugger": "warn",
22 | "no-delete-var": "warn",
23 | "no-dupe-class-members": "warn",
24 | "no-dupe-else-if": "warn",
25 | "no-dupe-keys": "warn",
26 | "no-duplicate-case": "warn",
27 | "no-empty-character-class": "warn",
28 | "no-empty-pattern": "warn",
29 | "no-empty-static-block": "warn",
30 | "no-eval": "warn",
31 | "no-ex-assign": "warn",
32 | "no-extra-boolean-cast": "warn",
33 | "no-func-assign": "warn",
34 | "no-global-assign": "warn",
35 | "no-import-assign": "warn",
36 | "no-invalid-regexp": "warn",
37 | "no-irregular-whitespace": "warn",
38 | "no-loss-of-precision": "warn",
39 | "no-new-native-nonconstructor": "warn",
40 | "no-nonoctal-decimal-escape": "warn",
41 | "no-obj-calls": "warn",
42 | "no-self-assign": "warn",
43 | "no-setter-return": "warn",
44 | "no-shadow-restricted-names": "warn",
45 | "no-sparse-arrays": "warn",
46 | "no-this-before-super": "warn",
47 | "no-unsafe-finally": "warn",
48 | "no-unsafe-negation": "warn",
49 | "no-unsafe-optional-chaining": "warn",
50 | "no-unused-labels": "warn",
51 | "no-unused-private-class-members": "warn",
52 | "no-unused-vars": "warn",
53 | "no-useless-catch": "warn",
54 | "no-useless-escape": "warn",
55 | "no-useless-rename": "warn",
56 | "no-with": "warn",
57 | "require-yield": "warn",
58 | "use-isnan": "warn",
59 | "valid-typeof": "warn",
60 | "oxc/bad-array-method-on-arguments": "warn",
61 | "oxc/bad-char-at-comparison": "warn",
62 | "oxc/bad-comparison-sequence": "warn",
63 | "oxc/bad-min-max-func": "warn",
64 | "oxc/bad-object-literal-comparison": "warn",
65 | "oxc/bad-replace-all-arg": "warn",
66 | "oxc/const-comparisons": "warn",
67 | "oxc/double-comparisons": "warn",
68 | "oxc/erasing-op": "warn",
69 | "oxc/missing-throw": "warn",
70 | "oxc/number-arg-out-of-range": "warn",
71 | "oxc/only-used-in-recursion": "warn",
72 | "oxc/uninvoked-array-callback": "warn",
73 | "react/jsx-key": "warn",
74 | "react/jsx-no-duplicate-props": "warn",
75 | "react/jsx-no-target-blank": "warn",
76 | "react/jsx-no-undef": "warn",
77 | "react/jsx-props-no-spread-multi": "warn",
78 | "react/no-children-prop": "warn",
79 | "react/no-danger-with-children": "warn",
80 | "react/no-direct-mutation-state": "warn",
81 | "react/no-find-dom-node": "warn",
82 | "react/no-is-mounted": "warn",
83 | "react/no-render-return-value": "warn",
84 | "react/no-string-refs": "warn",
85 | "react/void-dom-elements-no-children": "warn",
86 | "typescript/no-duplicate-enum-values": "warn",
87 | "typescript/no-extra-non-null-assertion": "warn",
88 | "typescript/no-misused-new": "warn",
89 | "typescript/no-non-null-asserted-optional-chain": "warn",
90 | "typescript/no-this-alias": "warn",
91 | "typescript/no-unnecessary-parameter-property-assignment": "warn",
92 | "typescript/no-unsafe-declaration-merging": "warn",
93 | "typescript/no-useless-empty-export": "warn",
94 | "typescript/no-wrapper-object-types": "warn",
95 | "typescript/prefer-as-const": "warn",
96 | "typescript/triple-slash-reference": "warn",
97 | "unicorn/no-await-in-promise-methods": "warn",
98 | "unicorn/no-empty-file": "warn",
99 | "unicorn/no-invalid-fetch-options": "warn",
100 | "unicorn/no-invalid-remove-event-listener": "warn",
101 | "unicorn/no-new-array": "warn",
102 | "unicorn/no-single-promise-in-promise-methods": "warn",
103 | "unicorn/no-thenable": "warn",
104 | "unicorn/no-unnecessary-await": "warn",
105 | "unicorn/no-useless-fallback-in-spread": "warn",
106 | "unicorn/no-useless-length-check": "warn",
107 | "unicorn/no-useless-spread": "warn",
108 | "unicorn/prefer-set-size": "warn",
109 | "unicorn/prefer-string-starts-ends-with": "warn"
110 | },
111 | "settings": {
112 | "jsx-a11y": {
113 | "polymorphicPropName": null,
114 | "components": {}
115 | },
116 | "next": {
117 | "rootDir": []
118 | },
119 | "react": {
120 | "formComponents": [],
121 | "linkComponents": []
122 | },
123 | "jsdoc": {
124 | "ignorePrivate": false,
125 | "ignoreInternal": false,
126 | "ignoreReplacesDocs": true,
127 | "overrideReplacesDocs": true,
128 | "augmentsExtendsReplacesDocs": false,
129 | "implementsReplacesDocs": false,
130 | "exemptDestructuredRootsFromChecks": false,
131 | "tagNamePreference": {}
132 | }
133 | },
134 | "env": {
135 | "builtin": true
136 | },
137 | "globals": {},
138 | "ignorePatterns": []
139 | }
--------------------------------------------------------------------------------
/sandbox/oxlint/nested-configs/package-c/.oxlintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": [
3 | "react",
4 | "unicorn",
5 | "typescript",
6 | "oxc"
7 | ],
8 | "categories": {},
9 | "rules": {
10 | "curly": "deny",
11 | "for-direction": "warn",
12 | "no-async-promise-executor": "warn",
13 | "no-caller": "warn",
14 | "no-class-assign": "warn",
15 | "no-compare-neg-zero": "warn",
16 | "no-cond-assign": "warn",
17 | "no-const-assign": "warn",
18 | "no-constant-binary-expression": "warn",
19 | "no-constant-condition": "warn",
20 | "no-control-regex": "warn",
21 | "no-debugger": "warn",
22 | "no-delete-var": "warn",
23 | "no-dupe-class-members": "warn",
24 | "no-dupe-else-if": "warn",
25 | "no-dupe-keys": "warn",
26 | "no-duplicate-case": "warn",
27 | "no-empty-character-class": "warn",
28 | "no-empty-pattern": "warn",
29 | "no-empty-static-block": "warn",
30 | "no-eval": "warn",
31 | "no-ex-assign": "warn",
32 | "no-extra-boolean-cast": "warn",
33 | "no-func-assign": "warn",
34 | "no-global-assign": "warn",
35 | "no-import-assign": "warn",
36 | "no-invalid-regexp": "warn",
37 | "no-irregular-whitespace": "warn",
38 | "no-loss-of-precision": "warn",
39 | "no-new-native-nonconstructor": "warn",
40 | "no-nonoctal-decimal-escape": "warn",
41 | "no-obj-calls": "warn",
42 | "no-self-assign": "warn",
43 | "no-setter-return": "warn",
44 | "no-shadow-restricted-names": "warn",
45 | "no-sparse-arrays": "warn",
46 | "no-this-before-super": "warn",
47 | "no-unsafe-finally": "warn",
48 | "no-unsafe-negation": "warn",
49 | "no-unsafe-optional-chaining": "warn",
50 | "no-unused-labels": "warn",
51 | "no-unused-private-class-members": "warn",
52 | "no-unused-vars": "warn",
53 | "no-useless-catch": "warn",
54 | "no-useless-escape": "warn",
55 | "no-useless-rename": "warn",
56 | "no-with": "warn",
57 | "require-yield": "warn",
58 | "use-isnan": "warn",
59 | "valid-typeof": "warn",
60 | "oxc/bad-array-method-on-arguments": "warn",
61 | "oxc/bad-char-at-comparison": "warn",
62 | "oxc/bad-comparison-sequence": "warn",
63 | "oxc/bad-min-max-func": "warn",
64 | "oxc/bad-object-literal-comparison": "warn",
65 | "oxc/bad-replace-all-arg": "warn",
66 | "oxc/const-comparisons": "warn",
67 | "oxc/double-comparisons": "warn",
68 | "oxc/erasing-op": "warn",
69 | "oxc/missing-throw": "warn",
70 | "oxc/number-arg-out-of-range": "warn",
71 | "oxc/only-used-in-recursion": "warn",
72 | "oxc/uninvoked-array-callback": "warn",
73 | "react/jsx-key": "warn",
74 | "react/jsx-no-duplicate-props": "warn",
75 | "react/jsx-no-target-blank": "warn",
76 | "react/jsx-no-undef": "warn",
77 | "react/jsx-props-no-spread-multi": "warn",
78 | "react/no-children-prop": "warn",
79 | "react/no-danger-with-children": "warn",
80 | "react/no-direct-mutation-state": "warn",
81 | "react/no-find-dom-node": "warn",
82 | "react/no-is-mounted": "warn",
83 | "react/no-render-return-value": "warn",
84 | "react/no-string-refs": "warn",
85 | "react/void-dom-elements-no-children": "warn",
86 | "typescript/no-duplicate-enum-values": "warn",
87 | "typescript/no-extra-non-null-assertion": "warn",
88 | "typescript/no-misused-new": "warn",
89 | "typescript/no-non-null-asserted-optional-chain": "warn",
90 | "typescript/no-this-alias": "warn",
91 | "typescript/no-unnecessary-parameter-property-assignment": "warn",
92 | "typescript/no-unsafe-declaration-merging": "warn",
93 | "typescript/no-useless-empty-export": "warn",
94 | "typescript/no-wrapper-object-types": "warn",
95 | "typescript/prefer-as-const": "warn",
96 | "typescript/triple-slash-reference": "warn",
97 | "unicorn/no-await-in-promise-methods": "warn",
98 | "unicorn/no-empty-file": "warn",
99 | "unicorn/no-invalid-fetch-options": "warn",
100 | "unicorn/no-invalid-remove-event-listener": "warn",
101 | "unicorn/no-new-array": "warn",
102 | "unicorn/no-single-promise-in-promise-methods": "warn",
103 | "unicorn/no-thenable": "warn",
104 | "unicorn/no-unnecessary-await": "warn",
105 | "unicorn/no-useless-fallback-in-spread": "warn",
106 | "unicorn/no-useless-length-check": "warn",
107 | "unicorn/no-useless-spread": "warn",
108 | "unicorn/prefer-set-size": "warn",
109 | "unicorn/prefer-string-starts-ends-with": "warn"
110 | },
111 | "settings": {
112 | "jsx-a11y": {
113 | "polymorphicPropName": null,
114 | "components": {}
115 | },
116 | "next": {
117 | "rootDir": []
118 | },
119 | "react": {
120 | "formComponents": [],
121 | "linkComponents": []
122 | },
123 | "jsdoc": {
124 | "ignorePrivate": false,
125 | "ignoreInternal": false,
126 | "ignoreReplacesDocs": true,
127 | "overrideReplacesDocs": true,
128 | "augmentsExtendsReplacesDocs": false,
129 | "implementsReplacesDocs": false,
130 | "exemptDestructuredRootsFromChecks": false,
131 | "tagNamePreference": {}
132 | }
133 | },
134 | "env": {
135 | "builtin": true
136 | },
137 | "globals": {},
138 | "ignorePatterns": []
139 | }
--------------------------------------------------------------------------------
/src/test/testData/oxlint/highlighting/nested-config/.oxlintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "./node_modules/oxlint/configuration_schema.json",
3 | "plugins": [
4 | "react",
5 | "unicorn",
6 | "typescript",
7 | "oxc"
8 | ],
9 | "categories": {},
10 | "rules": {
11 | "curly": "deny",
12 | "for-direction": "warn",
13 | "no-async-promise-executor": "warn",
14 | "no-caller": "warn",
15 | "no-class-assign": "warn",
16 | "no-compare-neg-zero": "warn",
17 | "no-cond-assign": "warn",
18 | "no-const-assign": "warn",
19 | "no-constant-binary-expression": "warn",
20 | "no-constant-condition": "warn",
21 | "no-control-regex": "warn",
22 | "no-debugger": "warn",
23 | "no-delete-var": "warn",
24 | "no-dupe-class-members": "warn",
25 | "no-dupe-else-if": "warn",
26 | "no-dupe-keys": "warn",
27 | "no-duplicate-case": "warn",
28 | "no-empty-character-class": "warn",
29 | "no-empty-pattern": "warn",
30 | "no-empty-static-block": "warn",
31 | "no-eval": "warn",
32 | "no-ex-assign": "warn",
33 | "no-extra-boolean-cast": "warn",
34 | "no-func-assign": "warn",
35 | "no-global-assign": "warn",
36 | "no-import-assign": "warn",
37 | "no-invalid-regexp": "warn",
38 | "no-irregular-whitespace": "warn",
39 | "no-loss-of-precision": "warn",
40 | "no-new-native-nonconstructor": "warn",
41 | "no-nonoctal-decimal-escape": "warn",
42 | "no-obj-calls": "warn",
43 | "no-self-assign": "warn",
44 | "no-setter-return": "warn",
45 | "no-shadow-restricted-names": "warn",
46 | "no-sparse-arrays": "warn",
47 | "no-this-before-super": "warn",
48 | "no-unsafe-finally": "warn",
49 | "no-unsafe-negation": "warn",
50 | "no-unsafe-optional-chaining": "warn",
51 | "no-unused-labels": "warn",
52 | "no-unused-private-class-members": "warn",
53 | "no-unused-vars": "warn",
54 | "no-useless-catch": "warn",
55 | "no-useless-escape": "warn",
56 | "no-useless-rename": "warn",
57 | "no-with": "warn",
58 | "require-yield": "warn",
59 | "use-isnan": "warn",
60 | "valid-typeof": "warn",
61 | "oxc/bad-array-method-on-arguments": "warn",
62 | "oxc/bad-char-at-comparison": "warn",
63 | "oxc/bad-comparison-sequence": "warn",
64 | "oxc/bad-min-max-func": "warn",
65 | "oxc/bad-object-literal-comparison": "warn",
66 | "oxc/bad-replace-all-arg": "warn",
67 | "oxc/const-comparisons": "warn",
68 | "oxc/double-comparisons": "warn",
69 | "oxc/erasing-op": "warn",
70 | "oxc/missing-throw": "warn",
71 | "oxc/number-arg-out-of-range": "warn",
72 | "oxc/only-used-in-recursion": "warn",
73 | "oxc/uninvoked-array-callback": "warn",
74 | "react/jsx-key": "warn",
75 | "react/jsx-no-duplicate-props": "warn",
76 | "react/jsx-no-target-blank": "warn",
77 | "react/jsx-no-undef": "warn",
78 | "react/jsx-props-no-spread-multi": "warn",
79 | "react/no-children-prop": "warn",
80 | "react/no-danger-with-children": "warn",
81 | "react/no-direct-mutation-state": "warn",
82 | "react/no-find-dom-node": "warn",
83 | "react/no-is-mounted": "warn",
84 | "react/no-render-return-value": "warn",
85 | "react/no-string-refs": "warn",
86 | "react/void-dom-elements-no-children": "warn",
87 | "typescript/no-duplicate-enum-values": "warn",
88 | "typescript/no-extra-non-null-assertion": "warn",
89 | "typescript/no-misused-new": "warn",
90 | "typescript/no-non-null-asserted-optional-chain": "warn",
91 | "typescript/no-this-alias": "warn",
92 | "typescript/no-unnecessary-parameter-property-assignment": "warn",
93 | "typescript/no-unsafe-declaration-merging": "warn",
94 | "typescript/no-useless-empty-export": "warn",
95 | "typescript/no-wrapper-object-types": "warn",
96 | "typescript/prefer-as-const": "warn",
97 | "typescript/triple-slash-reference": "warn",
98 | "unicorn/no-await-in-promise-methods": "warn",
99 | "unicorn/no-empty-file": "warn",
100 | "unicorn/no-invalid-fetch-options": "warn",
101 | "unicorn/no-invalid-remove-event-listener": "warn",
102 | "unicorn/no-new-array": "warn",
103 | "unicorn/no-single-promise-in-promise-methods": "warn",
104 | "unicorn/no-thenable": "warn",
105 | "unicorn/no-unnecessary-await": "warn",
106 | "unicorn/no-useless-fallback-in-spread": "warn",
107 | "unicorn/no-useless-length-check": "warn",
108 | "unicorn/no-useless-spread": "warn",
109 | "unicorn/prefer-set-size": "warn",
110 | "unicorn/prefer-string-starts-ends-with": "warn"
111 | },
112 | "settings": {
113 | "jsx-a11y": {
114 | "polymorphicPropName": null,
115 | "components": {}
116 | },
117 | "next": {
118 | "rootDir": []
119 | },
120 | "react": {
121 | "formComponents": [],
122 | "linkComponents": []
123 | },
124 | "jsdoc": {
125 | "ignorePrivate": false,
126 | "ignoreInternal": false,
127 | "ignoreReplacesDocs": true,
128 | "overrideReplacesDocs": true,
129 | "augmentsExtendsReplacesDocs": false,
130 | "implementsReplacesDocs": false,
131 | "exemptDestructuredRootsFromChecks": false,
132 | "tagNamePreference": {}
133 | }
134 | },
135 | "env": {
136 | "builtin": true
137 | },
138 | "globals": {},
139 | "ignorePatterns": []
140 | }
141 |
--------------------------------------------------------------------------------
/sandbox/oxlint/custom-config/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "oxc-lint",
3 | "version": "1.0.0",
4 | "lockfileVersion": 3,
5 | "requires": true,
6 | "packages": {
7 | "": {
8 | "name": "oxc-lint",
9 | "version": "1.0.0",
10 | "devDependencies": {
11 | "oxlint": "1.34.0"
12 | }
13 | },
14 | "node_modules/@oxlint/darwin-arm64": {
15 | "version": "1.34.0",
16 | "resolved": "https://registry.npmjs.org/@oxlint/darwin-arm64/-/darwin-arm64-1.34.0.tgz",
17 | "integrity": "sha512-euz3Dtp5/UE9axFkQnllOWp3gOwoqaxfZPUUwiW8HBelqhI9PRMVIfQ/akmwl+G5XixQZIgXkXQ5SJxnb1+Qww==",
18 | "cpu": [
19 | "arm64"
20 | ],
21 | "dev": true,
22 | "license": "MIT",
23 | "optional": true,
24 | "os": [
25 | "darwin"
26 | ]
27 | },
28 | "node_modules/@oxlint/darwin-x64": {
29 | "version": "1.34.0",
30 | "resolved": "https://registry.npmjs.org/@oxlint/darwin-x64/-/darwin-x64-1.34.0.tgz",
31 | "integrity": "sha512-XpmNviE5KOnHkhmQPwJJIBs+mJkr0qItTZBN4dz+O3p9gWN+gCqi3CBP71RiVahZw4qAEQSgY4wro+z0kx+erg==",
32 | "cpu": [
33 | "x64"
34 | ],
35 | "dev": true,
36 | "license": "MIT",
37 | "optional": true,
38 | "os": [
39 | "darwin"
40 | ]
41 | },
42 | "node_modules/@oxlint/linux-arm64-gnu": {
43 | "version": "1.34.0",
44 | "resolved": "https://registry.npmjs.org/@oxlint/linux-arm64-gnu/-/linux-arm64-gnu-1.34.0.tgz",
45 | "integrity": "sha512-aCPdoEUGsJGF9y88vDYoaugG4IEGwSBa+usyuAvEVl3vTfuTmE0RDQEC1Z+WnJ3J/cIEpbgKOzS12VwbzFicjg==",
46 | "cpu": [
47 | "arm64"
48 | ],
49 | "dev": true,
50 | "license": "MIT",
51 | "optional": true,
52 | "os": [
53 | "linux"
54 | ]
55 | },
56 | "node_modules/@oxlint/linux-arm64-musl": {
57 | "version": "1.34.0",
58 | "resolved": "https://registry.npmjs.org/@oxlint/linux-arm64-musl/-/linux-arm64-musl-1.34.0.tgz",
59 | "integrity": "sha512-cMo72LQBFmdnVLRKLAHD94ZUBq5Z+aA9Y+RKzkjhCmJuef5ZAfKC24TJD/6c5LxGYzkwwmyySoQAHq5B69i3PQ==",
60 | "cpu": [
61 | "arm64"
62 | ],
63 | "dev": true,
64 | "license": "MIT",
65 | "optional": true,
66 | "os": [
67 | "linux"
68 | ]
69 | },
70 | "node_modules/@oxlint/linux-x64-gnu": {
71 | "version": "1.34.0",
72 | "resolved": "https://registry.npmjs.org/@oxlint/linux-x64-gnu/-/linux-x64-gnu-1.34.0.tgz",
73 | "integrity": "sha512-+9xFhhkqgNIysEh+uHvcba8v4UtL1YzxuyDS2wTLdWrkGvIllCx5WjJItt3K/AhwatciksgNEXSo2Hh4fcQRog==",
74 | "cpu": [
75 | "x64"
76 | ],
77 | "dev": true,
78 | "license": "MIT",
79 | "optional": true,
80 | "os": [
81 | "linux"
82 | ]
83 | },
84 | "node_modules/@oxlint/linux-x64-musl": {
85 | "version": "1.34.0",
86 | "resolved": "https://registry.npmjs.org/@oxlint/linux-x64-musl/-/linux-x64-musl-1.34.0.tgz",
87 | "integrity": "sha512-qa7TL2DfEDdMeSP5UiU5JMs6D2PW7ZJAQ0WZYTgqDV8BlZ6nMkIYVBVIk3QPxIfkyxvfJVbG1RB3PkSWDcfwpA==",
88 | "cpu": [
89 | "x64"
90 | ],
91 | "dev": true,
92 | "license": "MIT",
93 | "optional": true,
94 | "os": [
95 | "linux"
96 | ]
97 | },
98 | "node_modules/@oxlint/win32-arm64": {
99 | "version": "1.34.0",
100 | "resolved": "https://registry.npmjs.org/@oxlint/win32-arm64/-/win32-arm64-1.34.0.tgz",
101 | "integrity": "sha512-mSJumUveg1S3DiOgvsrVNAGuvenBbbC/zsfT1qhltT+GLhJ7RPBK2I/jz0fTdE+I7M9/as8yc0XJ/eY23y2amA==",
102 | "cpu": [
103 | "arm64"
104 | ],
105 | "dev": true,
106 | "license": "MIT",
107 | "optional": true,
108 | "os": [
109 | "win32"
110 | ]
111 | },
112 | "node_modules/@oxlint/win32-x64": {
113 | "version": "1.34.0",
114 | "resolved": "https://registry.npmjs.org/@oxlint/win32-x64/-/win32-x64-1.34.0.tgz",
115 | "integrity": "sha512-izsDDt5WY4FSISCkPRLUYQD1aRaaEJkPLtEZe3DlioSUdUVAdvVbE+BGllFqR16DWfTTwO/6K4jDeooxQzTMjw==",
116 | "cpu": [
117 | "x64"
118 | ],
119 | "dev": true,
120 | "license": "MIT",
121 | "optional": true,
122 | "os": [
123 | "win32"
124 | ]
125 | },
126 | "node_modules/oxlint": {
127 | "version": "1.34.0",
128 | "resolved": "https://registry.npmjs.org/oxlint/-/oxlint-1.34.0.tgz",
129 | "integrity": "sha512-Ni0N8wAiKlgaYkI/Yz4VrutfVIZgd2shDtS+loQxyBTwO8YUAnk3+g7OQ1cyI/aatHiFwvFNfV/uvZyHUtyPpA==",
130 | "dev": true,
131 | "license": "MIT",
132 | "bin": {
133 | "oxc_language_server": "bin/oxc_language_server",
134 | "oxlint": "bin/oxlint"
135 | },
136 | "engines": {
137 | "node": "^20.19.0 || >=22.12.0"
138 | },
139 | "funding": {
140 | "url": "https://github.com/sponsors/Boshen"
141 | },
142 | "optionalDependencies": {
143 | "@oxlint/darwin-arm64": "1.34.0",
144 | "@oxlint/darwin-x64": "1.34.0",
145 | "@oxlint/linux-arm64-gnu": "1.34.0",
146 | "@oxlint/linux-arm64-musl": "1.34.0",
147 | "@oxlint/linux-x64-gnu": "1.34.0",
148 | "@oxlint/linux-x64-musl": "1.34.0",
149 | "@oxlint/win32-arm64": "1.34.0",
150 | "@oxlint/win32-x64": "1.34.0"
151 | },
152 | "peerDependencies": {
153 | "oxlint-tsgolint": ">=0.9.2"
154 | },
155 | "peerDependenciesMeta": {
156 | "oxlint-tsgolint": {
157 | "optional": true
158 | }
159 | }
160 | }
161 | }
162 | }
163 |
--------------------------------------------------------------------------------
/sandbox/oxlint/nested-configs/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "oxc-lint",
3 | "version": "1.0.0",
4 | "lockfileVersion": 3,
5 | "requires": true,
6 | "packages": {
7 | "": {
8 | "name": "oxc-lint",
9 | "version": "1.0.0",
10 | "devDependencies": {
11 | "oxlint": "1.34.0"
12 | }
13 | },
14 | "node_modules/@oxlint/darwin-arm64": {
15 | "version": "1.34.0",
16 | "resolved": "https://registry.npmjs.org/@oxlint/darwin-arm64/-/darwin-arm64-1.34.0.tgz",
17 | "integrity": "sha512-euz3Dtp5/UE9axFkQnllOWp3gOwoqaxfZPUUwiW8HBelqhI9PRMVIfQ/akmwl+G5XixQZIgXkXQ5SJxnb1+Qww==",
18 | "cpu": [
19 | "arm64"
20 | ],
21 | "dev": true,
22 | "license": "MIT",
23 | "optional": true,
24 | "os": [
25 | "darwin"
26 | ]
27 | },
28 | "node_modules/@oxlint/darwin-x64": {
29 | "version": "1.34.0",
30 | "resolved": "https://registry.npmjs.org/@oxlint/darwin-x64/-/darwin-x64-1.34.0.tgz",
31 | "integrity": "sha512-XpmNviE5KOnHkhmQPwJJIBs+mJkr0qItTZBN4dz+O3p9gWN+gCqi3CBP71RiVahZw4qAEQSgY4wro+z0kx+erg==",
32 | "cpu": [
33 | "x64"
34 | ],
35 | "dev": true,
36 | "license": "MIT",
37 | "optional": true,
38 | "os": [
39 | "darwin"
40 | ]
41 | },
42 | "node_modules/@oxlint/linux-arm64-gnu": {
43 | "version": "1.34.0",
44 | "resolved": "https://registry.npmjs.org/@oxlint/linux-arm64-gnu/-/linux-arm64-gnu-1.34.0.tgz",
45 | "integrity": "sha512-aCPdoEUGsJGF9y88vDYoaugG4IEGwSBa+usyuAvEVl3vTfuTmE0RDQEC1Z+WnJ3J/cIEpbgKOzS12VwbzFicjg==",
46 | "cpu": [
47 | "arm64"
48 | ],
49 | "dev": true,
50 | "license": "MIT",
51 | "optional": true,
52 | "os": [
53 | "linux"
54 | ]
55 | },
56 | "node_modules/@oxlint/linux-arm64-musl": {
57 | "version": "1.34.0",
58 | "resolved": "https://registry.npmjs.org/@oxlint/linux-arm64-musl/-/linux-arm64-musl-1.34.0.tgz",
59 | "integrity": "sha512-cMo72LQBFmdnVLRKLAHD94ZUBq5Z+aA9Y+RKzkjhCmJuef5ZAfKC24TJD/6c5LxGYzkwwmyySoQAHq5B69i3PQ==",
60 | "cpu": [
61 | "arm64"
62 | ],
63 | "dev": true,
64 | "license": "MIT",
65 | "optional": true,
66 | "os": [
67 | "linux"
68 | ]
69 | },
70 | "node_modules/@oxlint/linux-x64-gnu": {
71 | "version": "1.34.0",
72 | "resolved": "https://registry.npmjs.org/@oxlint/linux-x64-gnu/-/linux-x64-gnu-1.34.0.tgz",
73 | "integrity": "sha512-+9xFhhkqgNIysEh+uHvcba8v4UtL1YzxuyDS2wTLdWrkGvIllCx5WjJItt3K/AhwatciksgNEXSo2Hh4fcQRog==",
74 | "cpu": [
75 | "x64"
76 | ],
77 | "dev": true,
78 | "license": "MIT",
79 | "optional": true,
80 | "os": [
81 | "linux"
82 | ]
83 | },
84 | "node_modules/@oxlint/linux-x64-musl": {
85 | "version": "1.34.0",
86 | "resolved": "https://registry.npmjs.org/@oxlint/linux-x64-musl/-/linux-x64-musl-1.34.0.tgz",
87 | "integrity": "sha512-qa7TL2DfEDdMeSP5UiU5JMs6D2PW7ZJAQ0WZYTgqDV8BlZ6nMkIYVBVIk3QPxIfkyxvfJVbG1RB3PkSWDcfwpA==",
88 | "cpu": [
89 | "x64"
90 | ],
91 | "dev": true,
92 | "license": "MIT",
93 | "optional": true,
94 | "os": [
95 | "linux"
96 | ]
97 | },
98 | "node_modules/@oxlint/win32-arm64": {
99 | "version": "1.34.0",
100 | "resolved": "https://registry.npmjs.org/@oxlint/win32-arm64/-/win32-arm64-1.34.0.tgz",
101 | "integrity": "sha512-mSJumUveg1S3DiOgvsrVNAGuvenBbbC/zsfT1qhltT+GLhJ7RPBK2I/jz0fTdE+I7M9/as8yc0XJ/eY23y2amA==",
102 | "cpu": [
103 | "arm64"
104 | ],
105 | "dev": true,
106 | "license": "MIT",
107 | "optional": true,
108 | "os": [
109 | "win32"
110 | ]
111 | },
112 | "node_modules/@oxlint/win32-x64": {
113 | "version": "1.34.0",
114 | "resolved": "https://registry.npmjs.org/@oxlint/win32-x64/-/win32-x64-1.34.0.tgz",
115 | "integrity": "sha512-izsDDt5WY4FSISCkPRLUYQD1aRaaEJkPLtEZe3DlioSUdUVAdvVbE+BGllFqR16DWfTTwO/6K4jDeooxQzTMjw==",
116 | "cpu": [
117 | "x64"
118 | ],
119 | "dev": true,
120 | "license": "MIT",
121 | "optional": true,
122 | "os": [
123 | "win32"
124 | ]
125 | },
126 | "node_modules/oxlint": {
127 | "version": "1.34.0",
128 | "resolved": "https://registry.npmjs.org/oxlint/-/oxlint-1.34.0.tgz",
129 | "integrity": "sha512-Ni0N8wAiKlgaYkI/Yz4VrutfVIZgd2shDtS+loQxyBTwO8YUAnk3+g7OQ1cyI/aatHiFwvFNfV/uvZyHUtyPpA==",
130 | "dev": true,
131 | "license": "MIT",
132 | "bin": {
133 | "oxc_language_server": "bin/oxc_language_server",
134 | "oxlint": "bin/oxlint"
135 | },
136 | "engines": {
137 | "node": "^20.19.0 || >=22.12.0"
138 | },
139 | "funding": {
140 | "url": "https://github.com/sponsors/Boshen"
141 | },
142 | "optionalDependencies": {
143 | "@oxlint/darwin-arm64": "1.34.0",
144 | "@oxlint/darwin-x64": "1.34.0",
145 | "@oxlint/linux-arm64-gnu": "1.34.0",
146 | "@oxlint/linux-arm64-musl": "1.34.0",
147 | "@oxlint/linux-x64-gnu": "1.34.0",
148 | "@oxlint/linux-x64-musl": "1.34.0",
149 | "@oxlint/win32-arm64": "1.34.0",
150 | "@oxlint/win32-x64": "1.34.0"
151 | },
152 | "peerDependencies": {
153 | "oxlint-tsgolint": ">=0.9.2"
154 | },
155 | "peerDependenciesMeta": {
156 | "oxlint-tsgolint": {
157 | "optional": true
158 | }
159 | }
160 | }
161 | }
162 | }
163 |
--------------------------------------------------------------------------------
/src/test/testData/oxlint/highlighting/no-config/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "oxc-lint",
3 | "version": "1.0.0",
4 | "lockfileVersion": 3,
5 | "requires": true,
6 | "packages": {
7 | "": {
8 | "name": "oxc-lint",
9 | "version": "1.0.0",
10 | "devDependencies": {
11 | "oxlint": "1.34.0"
12 | }
13 | },
14 | "node_modules/@oxlint/darwin-arm64": {
15 | "version": "1.34.0",
16 | "resolved": "https://registry.npmjs.org/@oxlint/darwin-arm64/-/darwin-arm64-1.34.0.tgz",
17 | "integrity": "sha512-euz3Dtp5/UE9axFkQnllOWp3gOwoqaxfZPUUwiW8HBelqhI9PRMVIfQ/akmwl+G5XixQZIgXkXQ5SJxnb1+Qww==",
18 | "cpu": [
19 | "arm64"
20 | ],
21 | "dev": true,
22 | "license": "MIT",
23 | "optional": true,
24 | "os": [
25 | "darwin"
26 | ]
27 | },
28 | "node_modules/@oxlint/darwin-x64": {
29 | "version": "1.34.0",
30 | "resolved": "https://registry.npmjs.org/@oxlint/darwin-x64/-/darwin-x64-1.34.0.tgz",
31 | "integrity": "sha512-XpmNviE5KOnHkhmQPwJJIBs+mJkr0qItTZBN4dz+O3p9gWN+gCqi3CBP71RiVahZw4qAEQSgY4wro+z0kx+erg==",
32 | "cpu": [
33 | "x64"
34 | ],
35 | "dev": true,
36 | "license": "MIT",
37 | "optional": true,
38 | "os": [
39 | "darwin"
40 | ]
41 | },
42 | "node_modules/@oxlint/linux-arm64-gnu": {
43 | "version": "1.34.0",
44 | "resolved": "https://registry.npmjs.org/@oxlint/linux-arm64-gnu/-/linux-arm64-gnu-1.34.0.tgz",
45 | "integrity": "sha512-aCPdoEUGsJGF9y88vDYoaugG4IEGwSBa+usyuAvEVl3vTfuTmE0RDQEC1Z+WnJ3J/cIEpbgKOzS12VwbzFicjg==",
46 | "cpu": [
47 | "arm64"
48 | ],
49 | "dev": true,
50 | "license": "MIT",
51 | "optional": true,
52 | "os": [
53 | "linux"
54 | ]
55 | },
56 | "node_modules/@oxlint/linux-arm64-musl": {
57 | "version": "1.34.0",
58 | "resolved": "https://registry.npmjs.org/@oxlint/linux-arm64-musl/-/linux-arm64-musl-1.34.0.tgz",
59 | "integrity": "sha512-cMo72LQBFmdnVLRKLAHD94ZUBq5Z+aA9Y+RKzkjhCmJuef5ZAfKC24TJD/6c5LxGYzkwwmyySoQAHq5B69i3PQ==",
60 | "cpu": [
61 | "arm64"
62 | ],
63 | "dev": true,
64 | "license": "MIT",
65 | "optional": true,
66 | "os": [
67 | "linux"
68 | ]
69 | },
70 | "node_modules/@oxlint/linux-x64-gnu": {
71 | "version": "1.34.0",
72 | "resolved": "https://registry.npmjs.org/@oxlint/linux-x64-gnu/-/linux-x64-gnu-1.34.0.tgz",
73 | "integrity": "sha512-+9xFhhkqgNIysEh+uHvcba8v4UtL1YzxuyDS2wTLdWrkGvIllCx5WjJItt3K/AhwatciksgNEXSo2Hh4fcQRog==",
74 | "cpu": [
75 | "x64"
76 | ],
77 | "dev": true,
78 | "license": "MIT",
79 | "optional": true,
80 | "os": [
81 | "linux"
82 | ]
83 | },
84 | "node_modules/@oxlint/linux-x64-musl": {
85 | "version": "1.34.0",
86 | "resolved": "https://registry.npmjs.org/@oxlint/linux-x64-musl/-/linux-x64-musl-1.34.0.tgz",
87 | "integrity": "sha512-qa7TL2DfEDdMeSP5UiU5JMs6D2PW7ZJAQ0WZYTgqDV8BlZ6nMkIYVBVIk3QPxIfkyxvfJVbG1RB3PkSWDcfwpA==",
88 | "cpu": [
89 | "x64"
90 | ],
91 | "dev": true,
92 | "license": "MIT",
93 | "optional": true,
94 | "os": [
95 | "linux"
96 | ]
97 | },
98 | "node_modules/@oxlint/win32-arm64": {
99 | "version": "1.34.0",
100 | "resolved": "https://registry.npmjs.org/@oxlint/win32-arm64/-/win32-arm64-1.34.0.tgz",
101 | "integrity": "sha512-mSJumUveg1S3DiOgvsrVNAGuvenBbbC/zsfT1qhltT+GLhJ7RPBK2I/jz0fTdE+I7M9/as8yc0XJ/eY23y2amA==",
102 | "cpu": [
103 | "arm64"
104 | ],
105 | "dev": true,
106 | "license": "MIT",
107 | "optional": true,
108 | "os": [
109 | "win32"
110 | ]
111 | },
112 | "node_modules/@oxlint/win32-x64": {
113 | "version": "1.34.0",
114 | "resolved": "https://registry.npmjs.org/@oxlint/win32-x64/-/win32-x64-1.34.0.tgz",
115 | "integrity": "sha512-izsDDt5WY4FSISCkPRLUYQD1aRaaEJkPLtEZe3DlioSUdUVAdvVbE+BGllFqR16DWfTTwO/6K4jDeooxQzTMjw==",
116 | "cpu": [
117 | "x64"
118 | ],
119 | "dev": true,
120 | "license": "MIT",
121 | "optional": true,
122 | "os": [
123 | "win32"
124 | ]
125 | },
126 | "node_modules/oxlint": {
127 | "version": "1.34.0",
128 | "resolved": "https://registry.npmjs.org/oxlint/-/oxlint-1.34.0.tgz",
129 | "integrity": "sha512-Ni0N8wAiKlgaYkI/Yz4VrutfVIZgd2shDtS+loQxyBTwO8YUAnk3+g7OQ1cyI/aatHiFwvFNfV/uvZyHUtyPpA==",
130 | "dev": true,
131 | "license": "MIT",
132 | "bin": {
133 | "oxc_language_server": "bin/oxc_language_server",
134 | "oxlint": "bin/oxlint"
135 | },
136 | "engines": {
137 | "node": "^20.19.0 || >=22.12.0"
138 | },
139 | "funding": {
140 | "url": "https://github.com/sponsors/Boshen"
141 | },
142 | "optionalDependencies": {
143 | "@oxlint/darwin-arm64": "1.34.0",
144 | "@oxlint/darwin-x64": "1.34.0",
145 | "@oxlint/linux-arm64-gnu": "1.34.0",
146 | "@oxlint/linux-arm64-musl": "1.34.0",
147 | "@oxlint/linux-x64-gnu": "1.34.0",
148 | "@oxlint/linux-x64-musl": "1.34.0",
149 | "@oxlint/win32-arm64": "1.34.0",
150 | "@oxlint/win32-x64": "1.34.0"
151 | },
152 | "peerDependencies": {
153 | "oxlint-tsgolint": ">=0.9.2"
154 | },
155 | "peerDependenciesMeta": {
156 | "oxlint-tsgolint": {
157 | "optional": true
158 | }
159 | }
160 | }
161 | }
162 | }
163 |
--------------------------------------------------------------------------------
/src/test/testData/oxlint/highlighting/custom-config/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "oxc-lint",
3 | "version": "1.0.0",
4 | "lockfileVersion": 3,
5 | "requires": true,
6 | "packages": {
7 | "": {
8 | "name": "oxc-lint",
9 | "version": "1.0.0",
10 | "devDependencies": {
11 | "oxlint": "1.34.0"
12 | }
13 | },
14 | "node_modules/@oxlint/darwin-arm64": {
15 | "version": "1.34.0",
16 | "resolved": "https://registry.npmjs.org/@oxlint/darwin-arm64/-/darwin-arm64-1.34.0.tgz",
17 | "integrity": "sha512-euz3Dtp5/UE9axFkQnllOWp3gOwoqaxfZPUUwiW8HBelqhI9PRMVIfQ/akmwl+G5XixQZIgXkXQ5SJxnb1+Qww==",
18 | "cpu": [
19 | "arm64"
20 | ],
21 | "dev": true,
22 | "license": "MIT",
23 | "optional": true,
24 | "os": [
25 | "darwin"
26 | ]
27 | },
28 | "node_modules/@oxlint/darwin-x64": {
29 | "version": "1.34.0",
30 | "resolved": "https://registry.npmjs.org/@oxlint/darwin-x64/-/darwin-x64-1.34.0.tgz",
31 | "integrity": "sha512-XpmNviE5KOnHkhmQPwJJIBs+mJkr0qItTZBN4dz+O3p9gWN+gCqi3CBP71RiVahZw4qAEQSgY4wro+z0kx+erg==",
32 | "cpu": [
33 | "x64"
34 | ],
35 | "dev": true,
36 | "license": "MIT",
37 | "optional": true,
38 | "os": [
39 | "darwin"
40 | ]
41 | },
42 | "node_modules/@oxlint/linux-arm64-gnu": {
43 | "version": "1.34.0",
44 | "resolved": "https://registry.npmjs.org/@oxlint/linux-arm64-gnu/-/linux-arm64-gnu-1.34.0.tgz",
45 | "integrity": "sha512-aCPdoEUGsJGF9y88vDYoaugG4IEGwSBa+usyuAvEVl3vTfuTmE0RDQEC1Z+WnJ3J/cIEpbgKOzS12VwbzFicjg==",
46 | "cpu": [
47 | "arm64"
48 | ],
49 | "dev": true,
50 | "license": "MIT",
51 | "optional": true,
52 | "os": [
53 | "linux"
54 | ]
55 | },
56 | "node_modules/@oxlint/linux-arm64-musl": {
57 | "version": "1.34.0",
58 | "resolved": "https://registry.npmjs.org/@oxlint/linux-arm64-musl/-/linux-arm64-musl-1.34.0.tgz",
59 | "integrity": "sha512-cMo72LQBFmdnVLRKLAHD94ZUBq5Z+aA9Y+RKzkjhCmJuef5ZAfKC24TJD/6c5LxGYzkwwmyySoQAHq5B69i3PQ==",
60 | "cpu": [
61 | "arm64"
62 | ],
63 | "dev": true,
64 | "license": "MIT",
65 | "optional": true,
66 | "os": [
67 | "linux"
68 | ]
69 | },
70 | "node_modules/@oxlint/linux-x64-gnu": {
71 | "version": "1.34.0",
72 | "resolved": "https://registry.npmjs.org/@oxlint/linux-x64-gnu/-/linux-x64-gnu-1.34.0.tgz",
73 | "integrity": "sha512-+9xFhhkqgNIysEh+uHvcba8v4UtL1YzxuyDS2wTLdWrkGvIllCx5WjJItt3K/AhwatciksgNEXSo2Hh4fcQRog==",
74 | "cpu": [
75 | "x64"
76 | ],
77 | "dev": true,
78 | "license": "MIT",
79 | "optional": true,
80 | "os": [
81 | "linux"
82 | ]
83 | },
84 | "node_modules/@oxlint/linux-x64-musl": {
85 | "version": "1.34.0",
86 | "resolved": "https://registry.npmjs.org/@oxlint/linux-x64-musl/-/linux-x64-musl-1.34.0.tgz",
87 | "integrity": "sha512-qa7TL2DfEDdMeSP5UiU5JMs6D2PW7ZJAQ0WZYTgqDV8BlZ6nMkIYVBVIk3QPxIfkyxvfJVbG1RB3PkSWDcfwpA==",
88 | "cpu": [
89 | "x64"
90 | ],
91 | "dev": true,
92 | "license": "MIT",
93 | "optional": true,
94 | "os": [
95 | "linux"
96 | ]
97 | },
98 | "node_modules/@oxlint/win32-arm64": {
99 | "version": "1.34.0",
100 | "resolved": "https://registry.npmjs.org/@oxlint/win32-arm64/-/win32-arm64-1.34.0.tgz",
101 | "integrity": "sha512-mSJumUveg1S3DiOgvsrVNAGuvenBbbC/zsfT1qhltT+GLhJ7RPBK2I/jz0fTdE+I7M9/as8yc0XJ/eY23y2amA==",
102 | "cpu": [
103 | "arm64"
104 | ],
105 | "dev": true,
106 | "license": "MIT",
107 | "optional": true,
108 | "os": [
109 | "win32"
110 | ]
111 | },
112 | "node_modules/@oxlint/win32-x64": {
113 | "version": "1.34.0",
114 | "resolved": "https://registry.npmjs.org/@oxlint/win32-x64/-/win32-x64-1.34.0.tgz",
115 | "integrity": "sha512-izsDDt5WY4FSISCkPRLUYQD1aRaaEJkPLtEZe3DlioSUdUVAdvVbE+BGllFqR16DWfTTwO/6K4jDeooxQzTMjw==",
116 | "cpu": [
117 | "x64"
118 | ],
119 | "dev": true,
120 | "license": "MIT",
121 | "optional": true,
122 | "os": [
123 | "win32"
124 | ]
125 | },
126 | "node_modules/oxlint": {
127 | "version": "1.34.0",
128 | "resolved": "https://registry.npmjs.org/oxlint/-/oxlint-1.34.0.tgz",
129 | "integrity": "sha512-Ni0N8wAiKlgaYkI/Yz4VrutfVIZgd2shDtS+loQxyBTwO8YUAnk3+g7OQ1cyI/aatHiFwvFNfV/uvZyHUtyPpA==",
130 | "dev": true,
131 | "license": "MIT",
132 | "bin": {
133 | "oxc_language_server": "bin/oxc_language_server",
134 | "oxlint": "bin/oxlint"
135 | },
136 | "engines": {
137 | "node": "^20.19.0 || >=22.12.0"
138 | },
139 | "funding": {
140 | "url": "https://github.com/sponsors/Boshen"
141 | },
142 | "optionalDependencies": {
143 | "@oxlint/darwin-arm64": "1.34.0",
144 | "@oxlint/darwin-x64": "1.34.0",
145 | "@oxlint/linux-arm64-gnu": "1.34.0",
146 | "@oxlint/linux-arm64-musl": "1.34.0",
147 | "@oxlint/linux-x64-gnu": "1.34.0",
148 | "@oxlint/linux-x64-musl": "1.34.0",
149 | "@oxlint/win32-arm64": "1.34.0",
150 | "@oxlint/win32-x64": "1.34.0"
151 | },
152 | "peerDependencies": {
153 | "oxlint-tsgolint": ">=0.9.2"
154 | },
155 | "peerDependenciesMeta": {
156 | "oxlint-tsgolint": {
157 | "optional": true
158 | }
159 | }
160 | }
161 | }
162 | }
163 |
--------------------------------------------------------------------------------
/src/test/testData/oxlint/highlighting/nested-config/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "oxc-lint",
3 | "version": "1.0.0",
4 | "lockfileVersion": 3,
5 | "requires": true,
6 | "packages": {
7 | "": {
8 | "name": "oxc-lint",
9 | "version": "1.0.0",
10 | "devDependencies": {
11 | "oxlint": "1.34.0"
12 | }
13 | },
14 | "node_modules/@oxlint/darwin-arm64": {
15 | "version": "1.34.0",
16 | "resolved": "https://registry.npmjs.org/@oxlint/darwin-arm64/-/darwin-arm64-1.34.0.tgz",
17 | "integrity": "sha512-euz3Dtp5/UE9axFkQnllOWp3gOwoqaxfZPUUwiW8HBelqhI9PRMVIfQ/akmwl+G5XixQZIgXkXQ5SJxnb1+Qww==",
18 | "cpu": [
19 | "arm64"
20 | ],
21 | "dev": true,
22 | "license": "MIT",
23 | "optional": true,
24 | "os": [
25 | "darwin"
26 | ]
27 | },
28 | "node_modules/@oxlint/darwin-x64": {
29 | "version": "1.34.0",
30 | "resolved": "https://registry.npmjs.org/@oxlint/darwin-x64/-/darwin-x64-1.34.0.tgz",
31 | "integrity": "sha512-XpmNviE5KOnHkhmQPwJJIBs+mJkr0qItTZBN4dz+O3p9gWN+gCqi3CBP71RiVahZw4qAEQSgY4wro+z0kx+erg==",
32 | "cpu": [
33 | "x64"
34 | ],
35 | "dev": true,
36 | "license": "MIT",
37 | "optional": true,
38 | "os": [
39 | "darwin"
40 | ]
41 | },
42 | "node_modules/@oxlint/linux-arm64-gnu": {
43 | "version": "1.34.0",
44 | "resolved": "https://registry.npmjs.org/@oxlint/linux-arm64-gnu/-/linux-arm64-gnu-1.34.0.tgz",
45 | "integrity": "sha512-aCPdoEUGsJGF9y88vDYoaugG4IEGwSBa+usyuAvEVl3vTfuTmE0RDQEC1Z+WnJ3J/cIEpbgKOzS12VwbzFicjg==",
46 | "cpu": [
47 | "arm64"
48 | ],
49 | "dev": true,
50 | "license": "MIT",
51 | "optional": true,
52 | "os": [
53 | "linux"
54 | ]
55 | },
56 | "node_modules/@oxlint/linux-arm64-musl": {
57 | "version": "1.34.0",
58 | "resolved": "https://registry.npmjs.org/@oxlint/linux-arm64-musl/-/linux-arm64-musl-1.34.0.tgz",
59 | "integrity": "sha512-cMo72LQBFmdnVLRKLAHD94ZUBq5Z+aA9Y+RKzkjhCmJuef5ZAfKC24TJD/6c5LxGYzkwwmyySoQAHq5B69i3PQ==",
60 | "cpu": [
61 | "arm64"
62 | ],
63 | "dev": true,
64 | "license": "MIT",
65 | "optional": true,
66 | "os": [
67 | "linux"
68 | ]
69 | },
70 | "node_modules/@oxlint/linux-x64-gnu": {
71 | "version": "1.34.0",
72 | "resolved": "https://registry.npmjs.org/@oxlint/linux-x64-gnu/-/linux-x64-gnu-1.34.0.tgz",
73 | "integrity": "sha512-+9xFhhkqgNIysEh+uHvcba8v4UtL1YzxuyDS2wTLdWrkGvIllCx5WjJItt3K/AhwatciksgNEXSo2Hh4fcQRog==",
74 | "cpu": [
75 | "x64"
76 | ],
77 | "dev": true,
78 | "license": "MIT",
79 | "optional": true,
80 | "os": [
81 | "linux"
82 | ]
83 | },
84 | "node_modules/@oxlint/linux-x64-musl": {
85 | "version": "1.34.0",
86 | "resolved": "https://registry.npmjs.org/@oxlint/linux-x64-musl/-/linux-x64-musl-1.34.0.tgz",
87 | "integrity": "sha512-qa7TL2DfEDdMeSP5UiU5JMs6D2PW7ZJAQ0WZYTgqDV8BlZ6nMkIYVBVIk3QPxIfkyxvfJVbG1RB3PkSWDcfwpA==",
88 | "cpu": [
89 | "x64"
90 | ],
91 | "dev": true,
92 | "license": "MIT",
93 | "optional": true,
94 | "os": [
95 | "linux"
96 | ]
97 | },
98 | "node_modules/@oxlint/win32-arm64": {
99 | "version": "1.34.0",
100 | "resolved": "https://registry.npmjs.org/@oxlint/win32-arm64/-/win32-arm64-1.34.0.tgz",
101 | "integrity": "sha512-mSJumUveg1S3DiOgvsrVNAGuvenBbbC/zsfT1qhltT+GLhJ7RPBK2I/jz0fTdE+I7M9/as8yc0XJ/eY23y2amA==",
102 | "cpu": [
103 | "arm64"
104 | ],
105 | "dev": true,
106 | "license": "MIT",
107 | "optional": true,
108 | "os": [
109 | "win32"
110 | ]
111 | },
112 | "node_modules/@oxlint/win32-x64": {
113 | "version": "1.34.0",
114 | "resolved": "https://registry.npmjs.org/@oxlint/win32-x64/-/win32-x64-1.34.0.tgz",
115 | "integrity": "sha512-izsDDt5WY4FSISCkPRLUYQD1aRaaEJkPLtEZe3DlioSUdUVAdvVbE+BGllFqR16DWfTTwO/6K4jDeooxQzTMjw==",
116 | "cpu": [
117 | "x64"
118 | ],
119 | "dev": true,
120 | "license": "MIT",
121 | "optional": true,
122 | "os": [
123 | "win32"
124 | ]
125 | },
126 | "node_modules/oxlint": {
127 | "version": "1.34.0",
128 | "resolved": "https://registry.npmjs.org/oxlint/-/oxlint-1.34.0.tgz",
129 | "integrity": "sha512-Ni0N8wAiKlgaYkI/Yz4VrutfVIZgd2shDtS+loQxyBTwO8YUAnk3+g7OQ1cyI/aatHiFwvFNfV/uvZyHUtyPpA==",
130 | "dev": true,
131 | "license": "MIT",
132 | "bin": {
133 | "oxc_language_server": "bin/oxc_language_server",
134 | "oxlint": "bin/oxlint"
135 | },
136 | "engines": {
137 | "node": "^20.19.0 || >=22.12.0"
138 | },
139 | "funding": {
140 | "url": "https://github.com/sponsors/Boshen"
141 | },
142 | "optionalDependencies": {
143 | "@oxlint/darwin-arm64": "1.34.0",
144 | "@oxlint/darwin-x64": "1.34.0",
145 | "@oxlint/linux-arm64-gnu": "1.34.0",
146 | "@oxlint/linux-arm64-musl": "1.34.0",
147 | "@oxlint/linux-x64-gnu": "1.34.0",
148 | "@oxlint/linux-x64-musl": "1.34.0",
149 | "@oxlint/win32-arm64": "1.34.0",
150 | "@oxlint/win32-x64": "1.34.0"
151 | },
152 | "peerDependencies": {
153 | "oxlint-tsgolint": ">=0.9.2"
154 | },
155 | "peerDependenciesMeta": {
156 | "oxlint-tsgolint": {
157 | "optional": true
158 | }
159 | }
160 | }
161 | }
162 | }
163 |
--------------------------------------------------------------------------------
/sandbox/oxfmt/nested-configs/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "oxfmt-nested-configs",
3 | "version": "1.0.0",
4 | "lockfileVersion": 3,
5 | "requires": true,
6 | "packages": {
7 | "": {
8 | "name": "oxfmt-nested-configs",
9 | "version": "1.0.0",
10 | "devDependencies": {
11 | "oxfmt": "0.19.0"
12 | }
13 | },
14 | "node_modules/@oxfmt/darwin-arm64": {
15 | "version": "0.19.0",
16 | "resolved": "https://registry.npmjs.org/@oxfmt/darwin-arm64/-/darwin-arm64-0.19.0.tgz",
17 | "integrity": "sha512-FfNpn3ximwbBZCaS8WL2vAXcFuQFQgvv/brO6D1WdmL4pnFOgfBIpFfkeFnKfdmdpxtg9O0wF8NTcdw5Iyl2Bg==",
18 | "cpu": [
19 | "arm64"
20 | ],
21 | "dev": true,
22 | "license": "MIT",
23 | "optional": true,
24 | "os": [
25 | "darwin"
26 | ]
27 | },
28 | "node_modules/@oxfmt/darwin-x64": {
29 | "version": "0.19.0",
30 | "resolved": "https://registry.npmjs.org/@oxfmt/darwin-x64/-/darwin-x64-0.19.0.tgz",
31 | "integrity": "sha512-31FWUHAgTdTzOslz0zoA60UDEdeZZpeM6wTzCdffetwLbHkK8ZuCuzd+DauHZPNlSU8G72iw4lF7Zve9pSkK9w==",
32 | "cpu": [
33 | "x64"
34 | ],
35 | "dev": true,
36 | "license": "MIT",
37 | "optional": true,
38 | "os": [
39 | "darwin"
40 | ]
41 | },
42 | "node_modules/@oxfmt/linux-arm64-gnu": {
43 | "version": "0.19.0",
44 | "resolved": "https://registry.npmjs.org/@oxfmt/linux-arm64-gnu/-/linux-arm64-gnu-0.19.0.tgz",
45 | "integrity": "sha512-BkU9h39xKj/8uko82uFDJUharM8VxZO+uXKglpBnEC8XxWRzXocVCX4wpLT/Tfk4NyBy6fRM9DeISdZvQKZCjA==",
46 | "cpu": [
47 | "arm64"
48 | ],
49 | "dev": true,
50 | "license": "MIT",
51 | "optional": true,
52 | "os": [
53 | "linux"
54 | ]
55 | },
56 | "node_modules/@oxfmt/linux-arm64-musl": {
57 | "version": "0.19.0",
58 | "resolved": "https://registry.npmjs.org/@oxfmt/linux-arm64-musl/-/linux-arm64-musl-0.19.0.tgz",
59 | "integrity": "sha512-wWYk6Z/3iC+0zZAUkVCcEHui/IsUqsl+GEm9o6H7oARPLisXajbwCQcmqYslUD7eK6OXdYoWriBkEvSX/5dU4A==",
60 | "cpu": [
61 | "arm64"
62 | ],
63 | "dev": true,
64 | "license": "MIT",
65 | "optional": true,
66 | "os": [
67 | "linux"
68 | ]
69 | },
70 | "node_modules/@oxfmt/linux-x64-gnu": {
71 | "version": "0.19.0",
72 | "resolved": "https://registry.npmjs.org/@oxfmt/linux-x64-gnu/-/linux-x64-gnu-0.19.0.tgz",
73 | "integrity": "sha512-EB/b3or437E3uDie8QxeU3eA502JcmR1koyIBcH9rFidY0cMik58xvw54tXCY3WpMRxEXf37aHZzUSDP3qJnZg==",
74 | "cpu": [
75 | "x64"
76 | ],
77 | "dev": true,
78 | "license": "MIT",
79 | "optional": true,
80 | "os": [
81 | "linux"
82 | ]
83 | },
84 | "node_modules/@oxfmt/linux-x64-musl": {
85 | "version": "0.19.0",
86 | "resolved": "https://registry.npmjs.org/@oxfmt/linux-x64-musl/-/linux-x64-musl-0.19.0.tgz",
87 | "integrity": "sha512-htMB45orYoa1oFSjSmoGgcBDsD47/0joDfqpa8TrTDI5qsW5kAedpFR5wSce8Is9oj7SJ07omhOj96P/QiekWA==",
88 | "cpu": [
89 | "x64"
90 | ],
91 | "dev": true,
92 | "license": "MIT",
93 | "optional": true,
94 | "os": [
95 | "linux"
96 | ]
97 | },
98 | "node_modules/@oxfmt/win32-arm64": {
99 | "version": "0.19.0",
100 | "resolved": "https://registry.npmjs.org/@oxfmt/win32-arm64/-/win32-arm64-0.19.0.tgz",
101 | "integrity": "sha512-x7+3Eh/VWdXEda+BUmAKYlhGrRJVera7RfWw47Yx8PJUGtNqBfeYGDbf0W59ceK8Z3bY3OinrmOO3d1jOuXzMQ==",
102 | "cpu": [
103 | "arm64"
104 | ],
105 | "dev": true,
106 | "license": "MIT",
107 | "optional": true,
108 | "os": [
109 | "win32"
110 | ]
111 | },
112 | "node_modules/@oxfmt/win32-x64": {
113 | "version": "0.19.0",
114 | "resolved": "https://registry.npmjs.org/@oxfmt/win32-x64/-/win32-x64-0.19.0.tgz",
115 | "integrity": "sha512-X+FKXBg2jx4CxF5SJs3xpx1msMw5JfxaGD5qBZYqlHGdryQsy6zUY+bQwDDcuy3Ic/WNGD8ZNEuggeYNE7jx/Q==",
116 | "cpu": [
117 | "x64"
118 | ],
119 | "dev": true,
120 | "license": "MIT",
121 | "optional": true,
122 | "os": [
123 | "win32"
124 | ]
125 | },
126 | "node_modules/oxfmt": {
127 | "version": "0.19.0",
128 | "resolved": "https://registry.npmjs.org/oxfmt/-/oxfmt-0.19.0.tgz",
129 | "integrity": "sha512-tPTa3j4kXdJBzBRlK9wR0/Lnd4J21rzg29cRr/VVqqfvdhZs6M+Q6TkL+rxI/IQpq8ZY8L3c+KZvga/RgeuMsg==",
130 | "dev": true,
131 | "license": "MIT",
132 | "dependencies": {
133 | "tinypool": "2.0.0"
134 | },
135 | "bin": {
136 | "oxfmt": "bin/oxfmt"
137 | },
138 | "engines": {
139 | "node": "^20.19.0 || >=22.12.0"
140 | },
141 | "funding": {
142 | "url": "https://github.com/sponsors/Boshen"
143 | },
144 | "optionalDependencies": {
145 | "@oxfmt/darwin-arm64": "0.19.0",
146 | "@oxfmt/darwin-x64": "0.19.0",
147 | "@oxfmt/linux-arm64-gnu": "0.19.0",
148 | "@oxfmt/linux-arm64-musl": "0.19.0",
149 | "@oxfmt/linux-x64-gnu": "0.19.0",
150 | "@oxfmt/linux-x64-musl": "0.19.0",
151 | "@oxfmt/win32-arm64": "0.19.0",
152 | "@oxfmt/win32-x64": "0.19.0"
153 | }
154 | },
155 | "node_modules/tinypool": {
156 | "version": "2.0.0",
157 | "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-2.0.0.tgz",
158 | "integrity": "sha512-/RX9RzeH2xU5ADE7n2Ykvmi9ED3FBGPAjw9u3zucrNNaEBIO0HPSYgL0NT7+3p147ojeSdaVu08F6hjpv31HJg==",
159 | "dev": true,
160 | "license": "MIT",
161 | "engines": {
162 | "node": "^20.0.0 || >=22.0.0"
163 | }
164 | }
165 | }
166 | }
167 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # oxc-intellij-plugin Changelog
4 |
5 | ## [Unreleased]
6 |
7 | ## [0.0.21] - 2025-12-09
8 |
9 | ### Added
10 |
11 | - Add support for Oxfmt.
12 | - Format on save
13 | - Reformat code action
14 | - Right click and apply quick fixes
15 | - New settings for Oxfmt separate from the Oxlint settings
16 |
17 | ### Fixed
18 |
19 | - Fixed WSL with PNPM usage.
20 |
21 | ## [0.0.20] - 2025-11-19
22 |
23 | ### Added
24 |
25 | - Added support for the upcoming 1.29.0 version of Oxlint which allows executing the language server through
26 | `oxlint --lsp`. When using automatic configuration, this will be accommodated without change when updating to
27 | a newer version of Oxlint. When using a manual configuration with a custom path to the language server, there is
28 | a new checkbox that will need to be checked after updating.
29 |
30 | ## [0.0.19] - 2025-11-02
31 |
32 | ### Changed
33 |
34 | - Replace flags configuration with dedicated UI components. The Oxc language server is shifting away from a generic
35 | flags approach and this matches it. This also provides a better UI to users by exposing these options directly.
36 |
37 | ## [0.0.18] - 2025-10-19
38 |
39 | ### Changed
40 |
41 | - `pluginUntilBuild` version is not specified anymore. Plugin will be marked as compatible with all future versions of
42 | an IntelliJ IDE. Individual versions will be marked as not compatible within the JetBrains Marketplace as needed.
43 |
44 | ## [0.0.17] - 2025-09-18
45 |
46 | ### Added
47 |
48 | - Update pluginUntilBuild version for compatibility with 2025.3 versions of IntelliJ.
49 |
50 | ## [0.0.16] - 2025-08-30
51 |
52 | ### Fixed
53 |
54 | - Fix working directory lookup when specifying a language server path that is not part of a node_modules installation.
55 |
56 | ## [0.0.15] - 2025-08-28
57 |
58 | ### Added
59 |
60 | - Add support for type aware rules.
61 | - Cleanup the configuration UI.
62 |
63 | ## [0.0.14] - 2025-08-28
64 |
65 | ### Added
66 |
67 | - Add support for configuring the severity of unused disable directives.
68 | - Add support for passing flags to the language server.
69 |
70 | ### Fixed
71 |
72 | - Fix `fixAllOnSave` property to save to the correct element instead of conflicting with `configPath`.
73 |
74 | ## [0.0.13] - 2025-07-04
75 |
76 | ### Fixed
77 |
78 | - Resolve multiple language servers being started when only one should be running.
79 | This resulted in duplicate diagnostics in the IDE.
80 | - Resolve language server not stopping when closing all relevant files.
81 | - Resolve language server starting when opening irrelevant files.
82 |
83 | ### Removed
84 |
85 | - Removed support for IDE versions 2024.3.
86 |
87 | ## [0.0.12] - 2025-06-24
88 |
89 | ### Added
90 |
91 | - Add support for 2025.2.* versions of IntelliJ.
92 |
93 | ## [0.0.11] - 2025-06-11
94 |
95 | ### Fixed
96 |
97 | - Use project root directory as the root when an Oxlint config file is not available.
98 |
99 | ## [0.0.10] - 2025-06-09
100 |
101 | ### Added
102 |
103 | - Add run trigger to config settings. Allow triggering Oxlint either on save or on type.
104 |
105 | ### Changed
106 |
107 | - Use the new Oxc Language Server config format. This requires Oxlint 0.16.11 or newer.
108 |
109 | ## [0.0.8] - 2025-05-19
110 |
111 | ### Added
112 |
113 | - Support specifying a manual Oxc language server path to the binary instead of the Node.js wrapper.
114 |
115 | ### Fixed
116 |
117 | - Only show "Apply Oxc Quick Fixes" when the plugin is enabled and the selected file is a supported file extension.
118 |
119 | ## [0.0.7] - 2025-05-12
120 |
121 | ### Added
122 |
123 | - Add quick fixes back to the file explorer.
124 |
125 | ## [0.0.6] - 2025-05-05
126 |
127 | ### Added
128 |
129 | - If the RUST_LOG env variable is undefined, pass either DEBUG or TRACE to the process when starting the language server
130 | for additional logging when the plugin is configured to output debug or trace logs.
131 |
132 | ### Fixed
133 |
134 | - Additional fixes to support custom Oxlint config files.
135 | - Make the language server config path optional when using the manual config.
136 |
137 | ## [0.0.5] - 2025-05-01
138 |
139 | ### Fixed
140 |
141 | - Fix support for custom Oxlint config files.
142 | - Remove quick fix from file explorer context menu until the action supports it.
143 |
144 | ## [0.0.4] - 2025-04-25
145 |
146 | ### Fixed
147 |
148 | - Fix diagnostic may not have a code causing a NullPointerException to be thrown.
149 | ...Same as before, missed one spot for this.
150 |
151 | ## [0.0.3] - 2025-04-22
152 |
153 | ### Changed
154 |
155 | - Improve the settings UI.
156 |
157 | ### Fixed
158 |
159 | - Fix diagnostic may not have a code causing a NullPointerException to be thrown.
160 | - Stop showing the server restart message when enabling/disabling the plugin setting.
161 |
162 | ## [0.0.2] - 2025-04-15
163 |
164 | ### Fixed
165 |
166 | - Fix Oxc icon size.
167 |
168 | ## [0.0.1] - 2025-04-12
169 |
170 | ### Added
171 |
172 | - Initial scaffold created from [IntelliJ Platform Plugin Template](https://github.com/JetBrains/intellij-platform-plugin-template)
173 | - Integration with Oxc language server for lint abilities (oxlint)
174 | - Highlighting for warnings or errors identified by Oxlint.
175 | - Quick fixes to fix a warning or error when possible.
176 | - Command to fix all auto-fixable content within the current text editor.
177 | - Automatically apply fixes on save.
178 | - Custom icons for Oxlint config files
179 | - Schema validation for `.oxlintrc.json` configuration files. (Note: Comments within the .oxlintrc.json
180 | file are supported, however they show as an error within the IDE due to jsonc not being supported by the IDE.)
181 |
182 | [Unreleased]: https://github.com/oxc-project/oxc-intellij-plugin/compare/v0.0.21...HEAD
183 | [0.0.21]: https://github.com/oxc-project/oxc-intellij-plugin/compare/v0.0.20...v0.0.21
184 | [0.0.20]: https://github.com/oxc-project/oxc-intellij-plugin/compare/v0.0.19...v0.0.20
185 | [0.0.19]: https://github.com/oxc-project/oxc-intellij-plugin/compare/v0.0.18...v0.0.19
186 | [0.0.18]: https://github.com/oxc-project/oxc-intellij-plugin/compare/v0.0.17...v0.0.18
187 | [0.0.17]: https://github.com/oxc-project/oxc-intellij-plugin/compare/v0.0.16...v0.0.17
188 | [0.0.16]: https://github.com/oxc-project/oxc-intellij-plugin/compare/v0.0.15...v0.0.16
189 | [0.0.15]: https://github.com/oxc-project/oxc-intellij-plugin/compare/v0.0.14...v0.0.15
190 | [0.0.14]: https://github.com/oxc-project/oxc-intellij-plugin/compare/v0.0.13...v0.0.14
191 | [0.0.13]: https://github.com/oxc-project/oxc-intellij-plugin/compare/v0.0.12...v0.0.13
192 | [0.0.12]: https://github.com/oxc-project/oxc-intellij-plugin/compare/v0.0.11...v0.0.12
193 | [0.0.11]: https://github.com/oxc-project/oxc-intellij-plugin/compare/v0.0.10...v0.0.11
194 | [0.0.10]: https://github.com/oxc-project/oxc-intellij-plugin/compare/v0.0.8...v0.0.10
195 | [0.0.8]: https://github.com/oxc-project/oxc-intellij-plugin/compare/v0.0.7...v0.0.8
196 | [0.0.7]: https://github.com/oxc-project/oxc-intellij-plugin/compare/v0.0.6...v0.0.7
197 | [0.0.6]: https://github.com/oxc-project/oxc-intellij-plugin/compare/v0.0.5...v0.0.6
198 | [0.0.5]: https://github.com/oxc-project/oxc-intellij-plugin/compare/v0.0.4...v0.0.5
199 | [0.0.4]: https://github.com/oxc-project/oxc-intellij-plugin/compare/v0.0.3...v0.0.4
200 | [0.0.3]: https://github.com/oxc-project/oxc-intellij-plugin/compare/v0.0.2...v0.0.3
201 | [0.0.2]: https://github.com/oxc-project/oxc-intellij-plugin/compare/v0.0.1...v0.0.2
202 | [0.0.1]: https://github.com/oxc-project/oxc-intellij-plugin/commits/v0.0.1
203 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/github/oxc/project/oxcintellijplugin/oxfmt/settings/OxfmtConfigurable.kt:
--------------------------------------------------------------------------------
1 | package com.github.oxc.project.oxcintellijplugin.oxfmt.settings
2 |
3 | import com.github.oxc.project.oxcintellijplugin.ConfigurationMode
4 | import com.github.oxc.project.oxcintellijplugin.oxfmt.OxfmtBundle
5 | import com.github.oxc.project.oxcintellijplugin.oxfmt.OxfmtPackage
6 | import com.github.oxc.project.oxcintellijplugin.oxfmt.services.OxfmtServerService
7 | import com.intellij.ide.actionsOnSave.ActionsOnSaveConfigurable
8 | import com.intellij.lang.javascript.JavaScriptBundle
9 | import com.intellij.openapi.application.ApplicationManager
10 | import com.intellij.openapi.application.ApplicationNamesInfo
11 | import com.intellij.openapi.options.BoundSearchableConfigurable
12 | import com.intellij.openapi.project.Project
13 | import com.intellij.openapi.ui.DialogPanel
14 | import com.intellij.openapi.ui.ValidationInfo
15 | import com.intellij.ui.ContextHelpLabel
16 | import com.intellij.ui.components.JBTextField
17 | import com.intellij.ui.dsl.builder.AlignX
18 | import com.intellij.ui.dsl.builder.BottomGap
19 | import com.intellij.ui.dsl.builder.MutableProperty
20 | import com.intellij.ui.dsl.builder.bindSelected
21 | import com.intellij.ui.dsl.builder.bindText
22 | import com.intellij.ui.dsl.builder.panel
23 | import com.intellij.ui.layout.not
24 | import com.intellij.ui.layout.selected
25 | import com.intellij.util.ui.JBUI
26 | import com.intellij.util.ui.UIUtil
27 | import javax.swing.JCheckBox
28 | import javax.swing.JRadioButton
29 | import javax.swing.event.HyperlinkEvent
30 |
31 | private const val HELP_TOPIC = "reference.settings.oxfmt"
32 |
33 | class OxfmtConfigurable(private val project: Project) :
34 | BoundSearchableConfigurable(OxfmtBundle.message("oxfmt.name"), HELP_TOPIC, CONFIGURABLE_ID) {
35 |
36 | lateinit var fixAllOnSaveCheckBox: JCheckBox
37 | lateinit var disabledConfiguration: JRadioButton
38 | private lateinit var automaticConfiguration: JRadioButton
39 | private lateinit var manualConfiguration: JRadioButton
40 | private lateinit var extensionsField: JBTextField
41 |
42 | override fun createPanel(): DialogPanel {
43 | val settings = OxfmtSettings.getInstance(project)
44 | val server = OxfmtServerService.getInstance(project)
45 |
46 | return panel {
47 | // *********************
48 | // Configuration mode row
49 | // *********************
50 | buttonsGroup {
51 | row {
52 | disabledConfiguration = radioButton(
53 | JavaScriptBundle.message("settings.javascript.linters.autodetect.disabled",
54 | displayName)).bindSelected(
55 | ConfigurationModeProperty(settings, ConfigurationMode.DISABLED)).component
56 | }
57 | row {
58 | automaticConfiguration = radioButton(JavaScriptBundle.message(
59 | "settings.javascript.linters.autodetect.configure.automatically",
60 | displayName)).bindSelected(
61 | ConfigurationModeProperty(settings, ConfigurationMode.AUTOMATIC)).component
62 |
63 | val detectAutomaticallyHelpText = JavaScriptBundle.message(
64 | "settings.javascript.linters.autodetect.configure.automatically.help.text",
65 | ApplicationNamesInfo.getInstance().fullProductName, displayName,
66 | "${OxfmtPackage.CONFIG_NAME}.json")
67 |
68 | val helpLabel = ContextHelpLabel.create(detectAutomaticallyHelpText)
69 | helpLabel.border = JBUI.Borders.emptyLeft(UIUtil.DEFAULT_HGAP)
70 | cell(helpLabel)
71 | }
72 | row {
73 | manualConfiguration = radioButton(JavaScriptBundle.message(
74 | "settings.javascript.linters.autodetect.configure.manually",
75 | displayName)).bindSelected(
76 | ConfigurationModeProperty(settings, ConfigurationMode.MANUAL)).component
77 | }
78 | }
79 |
80 | // *********************
81 | // Manual configuration row
82 | // *********************
83 | indent {
84 | panel {
85 | row(OxfmtBundle.message("oxfmt.settings.languageServerPath")) {
86 | @Suppress("UnstableApiUsage") textFieldWithBrowseButton(
87 | OxfmtBundle.message("oxfmt.settings.languageServerPath")) {
88 | it.path
89 | }.align(AlignX.FILL).bindText(settings::binaryPath)
90 | }.visibleIf(manualConfiguration.selected)
91 |
92 | row(OxfmtBundle.message("oxfmt.config.path.label")) {
93 | @Suppress("UnstableApiUsage") textFieldWithBrowseButton(
94 | OxfmtBundle.message("oxfmt.config.path.label"),
95 | project,
96 | ) { it.path }.align(AlignX.FILL).bindText(settings::configPath)
97 | }.visibleIf(manualConfiguration.selected)
98 | }
99 | }
100 |
101 | // *********************
102 | // Supported file extensions row
103 | // *********************
104 | row(OxfmtBundle.message("oxfmt.supported.extensions.label")) {
105 | val parse = { it: String ->
106 | it.split(",").map { it.trim() }.filter { it.isNotBlank() }.toMutableList()
107 | }
108 | val join = { it: List ->
109 | it.joinToString(",")
110 | }
111 |
112 | val extensionsFieldCell = expandableTextField(parse, join).align(AlignX.FILL)
113 | .bindText({ join(settings.supportedExtensions) }, { value ->
114 | settings.supportedExtensions = parse(value)
115 | }).validationOnInput {
116 | validateExtensions(it)
117 | }
118 |
119 | extensionsField = extensionsFieldCell.component
120 |
121 | // Add help text with a "Reset" link below the field
122 | extensionsFieldCell.comment(OxfmtBundle.message(
123 | "oxfmt.supported.extensions.comment") + " Reset to Defaults")
124 | extensionsFieldCell.comment!!.addHyperlinkListener { event ->
125 | if (event.eventType == HyperlinkEvent.EventType.ACTIVATED && event.description == "reset") {
126 | extensionsField.text = join(OxfmtSettingsState.DEFAULT_EXTENSION_LIST)
127 | }
128 | }
129 | }.bottomGap(BottomGap.MEDIUM).enabledIf(!disabledConfiguration.selected)
130 |
131 | // *********************
132 | // Apply fixes on save row
133 | // *********************
134 | row {
135 | fixAllOnSaveCheckBox = checkBox(
136 | OxfmtBundle.message("oxfmt.run.fix.all.on.save.label")).bindSelected(
137 | { settings.configurationMode != ConfigurationMode.DISABLED && settings.fixAllOnSave },
138 | { settings.fixAllOnSave = it },
139 | ).component
140 |
141 | val link = ActionsOnSaveConfigurable.createGoToActionsOnSavePageLink()
142 | cell(link)
143 | }.enabledIf(!disabledConfiguration.selected)
144 |
145 | onApply {
146 | if (project.isDefault) {
147 | return@onApply
148 | }
149 | ApplicationManager.getApplication().invokeLater {
150 | server.restartServer()
151 | }
152 | }
153 | }
154 | }
155 |
156 | private fun validateExtensions(field: JBTextField): ValidationInfo? {
157 | val input = field.text
158 | val extensions = input.split(",").map { it.trim() }
159 |
160 | val invalidExtension = extensions.find { !it.matches(Regex("^\\.[a-zA-Z0-9]+$")) }
161 | return if (invalidExtension != null) {
162 | ValidationInfo(
163 | "Invalid extension: $invalidExtension. Must start with '.' and contain only alphanumeric characters.",
164 | field)
165 | } else {
166 | null
167 | }
168 | }
169 |
170 | private class ConfigurationModeProperty(
171 | private val settings: OxfmtSettings,
172 | private val mode: ConfigurationMode,
173 | ) : MutableProperty {
174 |
175 | override fun get(): Boolean = settings.configurationMode == mode
176 |
177 | override fun set(value: Boolean) {
178 | if (value) {
179 | settings.configurationMode = mode
180 | }
181 | }
182 | }
183 |
184 | companion object {
185 |
186 | const val CONFIGURABLE_ID = "OxfmtConfigurable"
187 | }
188 | }
189 |
--------------------------------------------------------------------------------