Skip to content

Commit 02b73f0

Browse files
authoredJun 28, 2024··
Merge pull request #454 from koxudaxi/support_ruff_server
Support new LSP integration
2 parents 527a63a + 9d18844 commit 02b73f0

15 files changed

+94
-35
lines changed
 

‎CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# Changelog
22

33
## [Unreleased]
4+
- Support new LSP integration [[#454](https://github.com/koxudaxi/ruff-pycharm-plugin/pull/454)]
45

56
## [0.0.33] - 2024-04-07
67

‎README.md

+3-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,9 @@ See [documentation](https://koxudaxi.github.io/ruff-pycharm-plugin/) for more de
2626
- [x] Support `ruff` config file path as an option
2727
- [x] Detect `ruff` executable in Conda environment
2828
- [x] Detect `ruff` executable in WSL
29-
- [x] Support `ruff-lsp` with [LSP integration](https://blog.jetbrains.com/platform/2023/07/lsp-for-plugin-developers/) for PyCharm Pro/IDEA Ultimate [Experimental]
29+
- [x] Support the Ruff LSP with [LSP integration](https://blog.jetbrains.com/platform/2023/07/lsp-for-plugin-developers/) for PyCharm Pro/IDEA Ultimate [Experimental]
30+
- [x] `ruff-lsp` integration
31+
- [x] `ruff server` integration
3032
- [x] Live Config Reload: Automatically updates from `pyproject.toml` and `ruff.toml` without restarting
3133
- [x] Support `ruff format` for ruff version `0.0.289` or later [Experimental]
3234

‎docs/index.md

+3-1
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,9 @@ See [documentation](https://koxudaxi.github.io/ruff-pycharm-plugin/) for more de
5858
- [x] Support `ruff` config file path as an option
5959
- [x] Detect `ruff` executable in Conda environment
6060
- [x] Detect `ruff` executable in WSL
61-
- [x] Support `ruff-lsp` with [LSP integration](https://blog.jetbrains.com/platform/2023/07/lsp-for-plugin-developers/) for PyCharm Pro/IDEA Ultimate [Experimental]
61+
- [x] Support the Ruff LSP with [LSP integration](https://blog.jetbrains.com/platform/2023/07/lsp-for-plugin-developers/) for PyCharm Pro/IDEA Ultimate [Experimental]
62+
- [x] `ruff-lsp` integration
63+
- [x] `ruff server` integration
6264
- [x] Live Config Reload: Automatically updates from `pyproject.toml` and `ruff.toml` without restarting
6365
- [x] Support `ruff format` for ruff version `0.0.289` or later [Experimental]
6466

‎gradle.properties

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ pluginGroup = com.koxudaxi.ruff
44
pluginName = Ruff
55
pluginRepositoryUrl = https://github.com/koxudaxi/ruff-pycharm-plugin
66
# SemVer format -> https://semver.org
7-
pluginVersion = 0.0.33
7+
pluginVersion = 0.0.34
88

99
# Supported build number ranges and IntelliJ Platform versions -> https://plugins.jetbrains.com/docs/intellij/build-number-ranges.html
1010
pluginSinceBuild = 233.11799.241

‎src/com/koxudaxi/ruff/Ruff.kt

+1
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ val NO_FIX_FORMAT_ARGS = ARGS_BASE + listOf("--no-fix", "--format", "json")
8686
val NO_FIX_OUTPUT_FORMAT_ARGS = ARGS_BASE + listOf("--no-fix", "--output-format", "json")
8787
val FORMAT_ARGS = listOf("format", "--force-exclude", "--quiet")
8888
val FORMAT_CHECK_ARGS = FORMAT_ARGS + listOf("--check")
89+
val LSP_PREVIEW_ARGS = listOf("server", "--preview")
8990
val Project.FIX_ARGS: List<String>
9091
get() = when {
9192
RuffCacheService.hasCheck(this) == true -> CHECK + FIX_ARGS_BASE

‎src/com/koxudaxi/ruff/RuffCacheService.kt

+11-3
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,17 @@ class RuffCacheService(val project: Project) {
99
private var hasFormatter: Boolean? = null
1010
private var hasOutputFormat: Boolean? = null
1111
private var hasCheck: Boolean? = null
12+
private var hasLsp: Boolean? = null
1213

1314
fun getVersion(): RuffVersion? {
1415
return version
1516
}
16-
private fun setVersionFromCommand() =
17+
private inline fun setVersionFromCommand(crossinline action: () -> Unit) =
1718
executeOnPooledThread {
1819
val ruffVersion = runRuff(project, listOf("--version"), true)
1920
?.let { getOrPutVersionFromVersionCache(it) }
2021
setVersion(ruffVersion)
22+
action()
2123
}
2224

2325

@@ -42,14 +44,15 @@ class RuffCacheService(val project: Project) {
4244
hasFormatter = version?.hasFormatter
4345
hasOutputFormat = version?.hasOutputFormat
4446
hasCheck = version?.hasCheck
47+
hasLsp = version?.hasLsp
4548
}
4649

4750
internal fun clearVersion() {
4851
setVersion(null)
4952
}
5053

51-
internal fun setVersion() {
52-
return setVersionFromCommand()
54+
internal inline fun setVersion(crossinline action: () -> Unit) {
55+
return setVersionFromCommand(action)
5356
}
5457

5558
internal fun hasFormatter(): Boolean {
@@ -64,6 +67,9 @@ class RuffCacheService(val project: Project) {
6467
return hasCheck
6568
}
6669

70+
internal fun hasLsp(): Boolean? {
71+
return hasLsp
72+
}
6773
internal
6874
companion object {
6975
fun hasFormatter(project: Project): Boolean = getInstance(project).hasFormatter()
@@ -72,6 +78,8 @@ class RuffCacheService(val project: Project) {
7278

7379
fun hasCheck(project: Project): Boolean? = getInstance(project).hasCheck()
7480

81+
fun hasLsp(project: Project): Boolean? = getInstance(project).hasLsp()
82+
7583
internal fun getInstance(project: Project): RuffCacheService {
7684
return project.getService(RuffCacheService::class.java)
7785
}

‎src/com/koxudaxi/ruff/RuffConfigPanel.form

+10-2
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
</properties>
1111
<border type="none"/>
1212
<children>
13-
<grid id="da542" layout-manager="GridLayoutManager" row-count="8" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
13+
<grid id="da542" layout-manager="GridLayoutManager" row-count="9" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
1414
<margin top="0" left="0" bottom="0" right="0"/>
1515
<constraints>
1616
<grid row="0" column="0" row-span="1" col-span="2" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
@@ -74,12 +74,20 @@
7474
</component>
7575
<component id="d521d" class="javax.swing.JCheckBox" binding="useRuffFormatCheckBox">
7676
<constraints>
77-
<grid row="7" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
77+
<grid row="8" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
7878
</constraints>
7979
<properties>
8080
<text value="Use ruff format (Experimental) for version 0.0.289 or later"/>
8181
</properties>
8282
</component>
83+
<component id="36608" class="javax.swing.JCheckBox" binding="useRuffServerCheckBox">
84+
<constraints>
85+
<grid row="7" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
86+
</constraints>
87+
<properties>
88+
<text value="Use `ruff server` for LSP functionality (if ruff supports the server option, this is preferred over ruff-lsp)."/>
89+
</properties>
90+
</component>
8391
</children>
8492
</grid>
8593
<vspacer id="a33f9">

‎src/com/koxudaxi/ruff/RuffConfigPanel.kt

+5-1
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ class RuffConfigPanel(project: Project) {
3838
private lateinit var disableOnSaveOutsideOfProjectCheckBox: JCheckBox
3939
private lateinit var useRuffLspCheckBox: JCheckBox
4040
private lateinit var useRuffFormatCheckBox: JCheckBox
41+
private lateinit var useRuffServerCheckBox: JCheckBox
4142
init {
4243
val ruffConfigService = getInstance(project)
4344
runRuffOnSaveCheckBox.isSelected = ruffConfigService.runRuffOnSave
@@ -53,6 +54,8 @@ class RuffConfigPanel(project: Project) {
5354
useRuffLspCheckBox.isSelected = ruffConfigService.useRuffLsp
5455
useRuffFormatCheckBox.isEnabled = true
5556
useRuffFormatCheckBox.isSelected = ruffConfigService.useRuffFormat
57+
useRuffServerCheckBox.isEnabled = true
58+
useRuffServerCheckBox.isSelected = ruffConfigService.useRuffServer
5659
disableOnSaveOutsideOfProjectCheckBox.isSelected = ruffConfigService.disableOnSaveOutsideOfProject
5760
runRuffOnSaveCheckBox.addActionListener {
5861
disableOnSaveOutsideOfProjectCheckBox.isEnabled = runRuffOnSaveCheckBox.isSelected
@@ -178,7 +181,8 @@ class RuffConfigPanel(project: Project) {
178181
get() = disableOnSaveOutsideOfProjectCheckBox.isSelected
179182
val useRuffLsp: Boolean
180183
get() = useRuffLspCheckBox.isSelected
181-
184+
val useRuffServer: Boolean
185+
get() = useRuffServerCheckBox.isSelected
182186
val useRuffFormat: Boolean
183187
get() = useRuffFormatCheckBox.isSelected
184188

‎src/com/koxudaxi/ruff/RuffConfigService.kt

+1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ class RuffConfigService : PersistentStateComponent<RuffConfigService> {
2222
var ruffConfigPath: @SystemDependent String? = null
2323
var disableOnSaveOutsideOfProject: Boolean = true
2424
var useRuffLsp: Boolean = false
25+
var useRuffServer: Boolean = false
2526
var useRuffFormat: Boolean = false
2627

2728
override fun getState(): RuffConfigService {

‎src/com/koxudaxi/ruff/RuffConfigurable.kt

+13-6
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,8 @@ class RuffConfigurable internal constructor(val project: Project) : Configurable
3636
ruffConfigService.ruffConfigPath != configPanel.ruffConfigPath ||
3737
ruffConfigService.disableOnSaveOutsideOfProject != configPanel.disableOnSaveOutsideOfProject ||
3838
ruffConfigService.useRuffLsp != configPanel.useRuffLsp ||
39-
ruffConfigService.useRuffFormat != configPanel.useRuffFormat
40-
39+
ruffConfigService.useRuffFormat != configPanel.useRuffFormat ||
40+
ruffConfigService.useRuffServer != configPanel.useRuffServer
4141
}
4242

4343
override fun apply() {
@@ -50,21 +50,28 @@ class RuffConfigurable internal constructor(val project: Project) : Configurable
5050
ruffConfigService.ruffConfigPath = configPanel.ruffConfigPath
5151
ruffConfigService.disableOnSaveOutsideOfProject = configPanel.disableOnSaveOutsideOfProject
5252
ruffConfigService.useRuffFormat = configPanel.useRuffFormat
53-
ruffCacheService.setVersion()
54-
if (ruffConfigService.useRuffLsp != configPanel.useRuffLsp) {
53+
val configPanelUseRuffLsp = configPanel.useRuffLsp
54+
val configPanelUseRuffServer = configPanel.useRuffServer
55+
if (ruffConfigService.useRuffLsp != configPanelUseRuffLsp || ruffConfigService.useRuffServer != configPanelUseRuffServer) {
56+
ruffCacheService.setVersion{
5557
@Suppress("UnstableApiUsage")
5658
val lspServerManager = if (lspIsSupported) LspServerManager.getInstance(project) else null
5759
if (lspServerManager != null) {
58-
if (configPanel.useRuffLsp) {
60+
if (configPanelUseRuffLsp || configPanelUseRuffServer) {
5961
@Suppress("UnstableApiUsage")
6062
lspServerManager.startServersIfNeeded(RuffLspServerSupportProvider::class.java)
6163
} else {
6264
@Suppress("UnstableApiUsage")
6365
lspServerManager.stopServers(RuffLspServerSupportProvider::class.java)
6466
}
6567
}
66-
ruffConfigService.useRuffLsp = configPanel.useRuffLsp
68+
}
69+
} else {
70+
ruffCacheService.setVersion{}
6771
}
72+
73+
ruffConfigService.useRuffLsp = configPanel.useRuffLsp
74+
ruffConfigService.useRuffServer = configPanel.useRuffServer
6875
}
6976

7077
override fun disposeUIResources() {

‎src/com/koxudaxi/ruff/RuffExternalAnnotator.kt

+2-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@ class RuffExternalAnnotator :
3939
if (file !is PyFile) return null
4040
val project = file.project
4141
val config = RuffConfigService.getInstance(project)
42-
if (config.useRuffLsp) return null
42+
if (config.useRuffLsp && config.ruffLspExecutablePath is String) return null
43+
if (config.useRuffServer && RuffCacheService.hasLsp(project) == true) return null
4344
if (!file.isApplicableTo) return null
4445
val profile: InspectionProfile = InspectionProjectProfileManager.getInstance(project).currentProfile
4546
val key = HighlightDisplayKey.find(RuffInspection.INSPECTION_SHORT_NAME) ?: return null

‎src/com/koxudaxi/ruff/RuffLspServerSupportProvider.kt

+35-12
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import com.intellij.codeInspection.ex.InspectionToolRegistrar
22
import com.intellij.execution.configurations.GeneralCommandLine
33
import com.intellij.openapi.project.Project
44
import com.intellij.openapi.vfs.VirtualFile
5+
import com.intellij.platform.lsp.api.LspServerManager
56
import com.intellij.platform.lsp.api.LspServerSupportProvider
67
import com.intellij.platform.lsp.api.ProjectWideLspServerDescriptor
78
import com.intellij.platform.lsp.api.customization.LspCompletionSupport
@@ -20,14 +21,27 @@ class RuffLspServerSupportProvider : LspServerSupportProvider {
2021
serverStarter: LspServerSupportProvider.LspServerStarter
2122
) {
2223
val ruffConfigService = RuffConfigService.getInstance(project)
23-
if (!ruffConfigService.useRuffLsp) return
24+
if (!ruffConfigService.useRuffLsp && !ruffConfigService.useRuffServer) return
2425
if (!isInspectionEnabled(project)) return
2526
if (file.extension != "py") return
26-
val executable =
27-
ruffConfigService.ruffLspExecutablePath?.let { File(it) }?.takeIf { it.exists() } ?: detectRuffExecutable(
28-
project, ruffConfigService, true
29-
) ?: return
30-
serverStarter.ensureServerStarted(RuffLspServerDescriptor(project, executable, ruffConfigService))
27+
val ruffCacheService = RuffCacheService.getInstance(project)
28+
if (ruffCacheService.getVersion() == null) return
29+
val descriptor = when {
30+
ruffConfigService.useRuffServer && ruffCacheService.hasLsp() == true -> {
31+
getRuffExecutable(project, ruffConfigService , false)?.let { RuffServerServerDescriptor(project, it, ruffConfigService) }
32+
}
33+
else -> getRuffExecutable(project, ruffConfigService, true)?.let { RuffLspServerDescriptor(project, it, ruffConfigService) }
34+
} ?: return
35+
serverStarter.ensureServerStarted(descriptor)
36+
}
37+
38+
private fun getRuffExecutable(project: Project, ruffConfigService: RuffConfigService, lsp: Boolean): File? {
39+
return when {
40+
lsp -> ruffConfigService.ruffLspExecutablePath
41+
else -> ruffConfigService.ruffExecutablePath
42+
}?.let { File(it) }?.takeIf { it.exists() } ?: detectRuffExecutable(
43+
project, ruffConfigService, lsp
44+
)
3145
}
3246

3347
private fun isInspectionEnabled(project: Project): Boolean {
@@ -39,13 +53,9 @@ class RuffLspServerSupportProvider : LspServerSupportProvider {
3953
}
4054
}
4155

42-
4356
@Suppress("UnstableApiUsage")
44-
private class RuffLspServerDescriptor(project: Project, val executable: File, val ruffConfig: RuffConfigService) :
45-
ProjectWideLspServerDescriptor(project, "Ruff") {
46-
47-
override fun isSupportedFile(file: VirtualFile) = file.extension == "py"
48-
57+
private class RuffLspServerDescriptor(project: Project, executable: File, ruffConfig: RuffConfigService) :
58+
RuffLspServerDescriptorBase(project, executable, ruffConfig) {
4959
private fun createBaseCommandLine(): GeneralCommandLine = GeneralCommandLine(executable.absolutePath)
5060

5161
override fun createCommandLine(): GeneralCommandLine {
@@ -55,7 +65,15 @@ private class RuffLspServerDescriptor(project: Project, val executable: File, va
5565
}
5666
return commandLine
5767
}
68+
}
5869

70+
71+
@Suppress("UnstableApiUsage")
72+
abstract class RuffLspServerDescriptorBase(project: Project, val executable: File, val ruffConfig: RuffConfigService) :
73+
ProjectWideLspServerDescriptor(project, "Ruff") {
74+
75+
override fun isSupportedFile(file: VirtualFile) = file.extension == "py"
76+
abstract override fun createCommandLine(): GeneralCommandLine
5977
override fun createInitializationOptions(): Any? {
6078
val config = ruffConfig.ruffConfigPath?.let { File(it) }?.takeIf { it.exists() } ?: return null
6179
return InitOptions(Settings(listOf(CONFIG_ARG, config.absolutePath)))
@@ -65,6 +83,11 @@ private class RuffLspServerDescriptor(project: Project, val executable: File, va
6583
override val lspCompletionSupport: LspCompletionSupport? = null
6684
}
6785

86+
@Suppress("UnstableApiUsage")
87+
private class RuffServerServerDescriptor(project: Project, executable: File, ruffConfig: RuffConfigService) :
88+
RuffLspServerDescriptorBase(project, executable, ruffConfig) {
89+
override fun createCommandLine(): GeneralCommandLine = GeneralCommandLine(listOf(executable.absolutePath) + LSP_PREVIEW_ARGS)
90+
}
6891
@Serializable
6992
data class Settings(
7093
val args: List<String>

‎src/com/koxudaxi/ruff/RuffPackageManagerListener.kt

+3-2
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,14 @@ class RuffPackageManagerListener(project: Project) : PyPackageManager.Listener {
1414
override fun packagesRefreshed(sdk: Sdk) {
1515
ruffConfigService.projectRuffExecutablePath = findRuffExecutableInSDK(sdk, false)?.absolutePath
1616
ruffConfigService.projectRuffLspExecutablePath = findRuffExecutableInSDK(sdk, true)?.absolutePath
17-
ruffCacheService.setVersion()
18-
if (lspServerManager != null && ruffConfigService.useRuffLsp) {
17+
ruffCacheService.setVersion{
18+
if (lspServerManager != null && (ruffConfigService.useRuffLsp || ruffConfigService.useRuffServer)) {
1919
try {
2020
@Suppress("UnstableApiUsage")
2121
lspServerManager.stopAndRestartIfNeeded(RuffLspServerSupportProvider::class.java)
2222
} catch (_: Exception) {
2323
}
2424
}
25+
}
2526
}
2627
}

‎src/com/koxudaxi/ruff/RuffProjectInitializer.kt

+2-5
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ class RuffProjectInitializer : ProjectActivity {
2121
try {
2222
val ruffCacheService = RuffCacheService.getInstance(project)
2323
if (ruffCacheService.getVersion() == null) {
24-
ruffCacheService.setVersion()
24+
ruffCacheService.setVersion{}
2525
}
2626
if (lspIsSupported) {
2727
setUpPyProjectTomlLister(project)
@@ -56,11 +56,8 @@ class RuffProjectInitializer : ProjectActivity {
5656
) return
5757
ApplicationManager.getApplication().invokeLater {
5858
if (project.isDisposed) return@invokeLater
59-
if (!ruffConfigService.useRuffLsp) return@invokeLater
59+
if (!ruffConfigService.useRuffLsp && !ruffConfigService.useRuffServer) return@invokeLater
6060
lspServerManager.stopAndRestartIfNeeded(RuffLspServerSupportProvider::class.java)
61-
62-
LspServerManager.getInstance(project)
63-
.stopAndRestartIfNeeded(RuffLspServerSupportProvider::class.java)
6461
}
6562
} catch (_: AlreadyDisposedException) {
6663
}

‎src/com/koxudaxi/ruff/RuffVersion.kt

+3
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,13 @@ class RuffVersion(private val major: Int, private val minor: Int = 0, private va
1414
val hasOutputFormat: Boolean get() = this >= SUPPORT_OUTPUT_FORMAT_VERSION
1515

1616
val hasCheck: Boolean get() = this >= SUPPORT_CHECK_VERSION
17+
18+
val hasLsp: Boolean get() = this >= SUPPORT_LSP_VERSION
1719
companion object {
1820
val SUPPORT_FORMAT_VERSION = RuffVersion(0, 0, 289)
1921
val SUPPORT_OUTPUT_FORMAT_VERSION = RuffVersion(0, 0, 291)
2022
val SUPPORT_CHECK_VERSION = RuffVersion(0, 3, 0)
23+
val SUPPORT_LSP_VERSION = RuffVersion(0, 4, 5)
2124

2225
}
2326
}

0 commit comments

Comments
 (0)
Please sign in to comment.