chore: separate out lsp logic from core

This commit is contained in:
FalsePattern 2025-02-18 15:36:39 +01:00
parent 0b22a4538b
commit 0982b3488d
Signed by: falsepattern
GPG key ID: E930CDEC50C50E23
35 changed files with 291 additions and 50 deletions

View file

@ -110,6 +110,7 @@ dependencies {
runtimeOnly(project(":core"))
runtimeOnly(project(":cidr"))
runtimeOnly(project(":lsp"))
}
intellijPlatform {

View file

@ -7,8 +7,6 @@ plugins {
kotlin("plugin.serialization")
}
val lsp4ijVersion: String by project
val lsp4jVersion: String by project
val ideaCommunityVersion: String by project
val useInstaller = property("useInstaller").toString().toBoolean()
@ -16,8 +14,6 @@ dependencies {
intellijPlatform {
create(IntelliJPlatformType.IntellijIdeaCommunity, ideaCommunityVersion, useInstaller = useInstaller)
}
compileOnly("com.redhat.devtools.intellij:lsp4ij:$lsp4ijVersion")
compileOnly("org.eclipse.lsp4j:org.eclipse.lsp4j:$lsp4jVersion")
compileOnly("org.jetbrains.kotlinx:kotlinx-serialization-core:1.7.3")
}

View file

@ -25,7 +25,6 @@ package com.falsepattern.zigbrains
import com.falsepattern.zigbrains.direnv.DirenvCmd
import com.falsepattern.zigbrains.direnv.emptyEnv
import com.falsepattern.zigbrains.direnv.getDirenv
import com.falsepattern.zigbrains.lsp.settings.zlsSettings
import com.falsepattern.zigbrains.project.settings.zigProjectSettings
import com.falsepattern.zigbrains.project.toolchain.LocalZigToolchain
import com.falsepattern.zigbrains.project.toolchain.ZigToolchainProvider
@ -89,17 +88,6 @@ class ZBStartup: ProjectActivity {
project.zigProjectSettings.state = zigProjectState
}
}
val zlsState = project.zlsSettings.state
if (zlsState.zlsPath.isBlank()) {
val env = if (DirenvCmd.direnvInstalled() && !project.isDefault && zlsState.direnv)
project.getDirenv()
else
emptyEnv
env.findExecutableOnPATH("zls")?.let {
zlsState.zlsPath = it.pathString
project.zlsSettings.state = zlsState
}
}
}
}

View file

@ -22,8 +22,7 @@
package com.falsepattern.zigbrains.project.newproject
import com.falsepattern.zigbrains.lsp.settings.ZLSSettingsPanel
import com.falsepattern.zigbrains.project.settings.ZigProjectSettingsPanel
import com.falsepattern.zigbrains.project.settings.ZigProjectConfigurationProvider
import com.falsepattern.zigbrains.project.template.ZigExecutableTemplate
import com.falsepattern.zigbrains.project.template.ZigInitTemplate
import com.falsepattern.zigbrains.project.template.ZigLibraryTemplate
@ -44,8 +43,7 @@ import javax.swing.ListSelectionModel
class ZigNewProjectPanel(private var handleGit: Boolean): Disposable {
private val git = JBCheckBox()
private val projConf = ZigProjectSettingsPanel(null).also { Disposer.register(this, it) }
private val zlsConf = ZLSSettingsPanel(null).also { Disposer.register(this, it) }
private val panels = ZigProjectConfigurationProvider.createNewProjectSettingsPanels().onEach { Disposer.register(this, it) }
private val templateList = JBList(JBList.createDefaultListModel(defaultTemplates)).apply {
selectionMode = ListSelectionModel.SINGLE_SELECTION
selectedIndex = 0
@ -68,7 +66,7 @@ class ZigNewProjectPanel(private var handleGit: Boolean): Disposable {
fun getData(): ZigProjectConfigurationData {
val selectedTemplate = templateList.selectedValue
return ZigProjectConfigurationData(handleGit && git.isSelected, projConf.data, zlsConf.data, selectedTemplate)
return ZigProjectConfigurationData(handleGit && git.isSelected, panels.map { it.data }, selectedTemplate)
}
fun attach(p: Panel): Unit = with(p) {
@ -85,8 +83,7 @@ class ZigNewProjectPanel(private var handleGit: Boolean): Disposable {
.align(AlignY.FILL)
}
}
projConf.attach(p)
zlsConf.attach(p)
panels.forEach { it.attach(p) }
}
override fun dispose() {

View file

@ -22,10 +22,7 @@
package com.falsepattern.zigbrains.project.newproject
import com.falsepattern.zigbrains.lsp.settings.ZLSSettings
import com.falsepattern.zigbrains.lsp.settings.zlsSettings
import com.falsepattern.zigbrains.project.settings.ZigProjectSettings
import com.falsepattern.zigbrains.project.settings.zigProjectSettings
import com.falsepattern.zigbrains.project.settings.ZigProjectConfigurationProvider
import com.falsepattern.zigbrains.project.template.ZigInitTemplate
import com.falsepattern.zigbrains.project.template.ZigProjectTemplate
import com.falsepattern.zigbrains.shared.zigCoroutineScope
@ -45,21 +42,21 @@ import kotlinx.coroutines.launch
@JvmRecord
data class ZigProjectConfigurationData(
val git: Boolean,
val projConf: ZigProjectSettings,
val zlsConf: ZLSSettings,
val conf: List<ZigProjectConfigurationProvider.Settings>,
val selectedTemplate: ZigProjectTemplate
) {
@RequiresBackgroundThread
suspend fun generateProject(requestor: Any, project: Project, baseDir: VirtualFile, forceGitignore: Boolean): Boolean {
return reportProgress { reporter ->
project.zigProjectSettings.loadState(projConf)
project.zlsSettings.loadState(zlsConf)
conf.forEach { it.apply(project) }
val template = selectedTemplate
if (!reporter.indeterminateStep("Initializing project") {
if (template is ZigInitTemplate) {
val toolchain = projConf.toolchain ?: run {
val toolchain = conf
.mapNotNull { it as? ZigProjectConfigurationProvider.ToolchainProvider }
.firstNotNullOfOrNull { it.toolchain } ?: run {
Notification(
"zigbrains",
"Tried to generate project with zig init, but zig toolchain is invalid",

View file

@ -22,11 +22,10 @@
package com.falsepattern.zigbrains.project.settings
import com.falsepattern.zigbrains.lsp.settings.ZLSSettingsConfigurable
import com.falsepattern.zigbrains.shared.MultiConfigurable
import com.intellij.openapi.project.Project
class ZigConfigurable(project: Project): MultiConfigurable(ZigProjectConfigurable(project), ZLSSettingsConfigurable(project)) {
class ZigConfigurable(project: Project): MultiConfigurable(ZigProjectConfigurationProvider.createConfigurables(project)) {
override fun getDisplayName(): String {
return "Zig"
}

View file

@ -0,0 +1,42 @@
/*
* This file is part of ZigBrains.
*
* Copyright (C) 2023-2025 FalsePattern
* All Rights Reserved
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* ZigBrains is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, only version 3 of the License.
*
* ZigBrains is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with ZigBrains. If not, see <https://www.gnu.org/licenses/>.
*/
package com.falsepattern.zigbrains.project.settings
import com.falsepattern.zigbrains.shared.SubConfigurable
import com.intellij.openapi.project.Project
class ZigCoreProjectConfigurationProvider: ZigProjectConfigurationProvider {
override fun handleMainConfigChanged(project: Project) {
}
override fun createConfigurable(project: Project): SubConfigurable {
return ZigProjectConfigurable(project)
}
override fun createNewProjectSettingsPanel(): ZigProjectConfigurationProvider.SettingsPanel {
return ZigProjectSettingsPanel(null)
}
override val priority: Int
get() = 0
}

View file

@ -22,7 +22,6 @@
package com.falsepattern.zigbrains.project.settings
import com.falsepattern.zigbrains.lsp.startLSP
import com.falsepattern.zigbrains.shared.SubConfigurable
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.Disposer
@ -45,7 +44,7 @@ class ZigProjectConfigurable(private val project: Project): SubConfigurable {
val modified = service.isModified(data)
service.state = data
if (modified) {
startLSP(project, true)
ZigProjectConfigurationProvider.mainConfigChanged(project)
}
}

View file

@ -0,0 +1,59 @@
/*
* This file is part of ZigBrains.
*
* Copyright (C) 2023-2025 FalsePattern
* All Rights Reserved
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* ZigBrains is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, only version 3 of the License.
*
* ZigBrains is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with ZigBrains. If not, see <https://www.gnu.org/licenses/>.
*/
package com.falsepattern.zigbrains.project.settings
import com.falsepattern.zigbrains.project.toolchain.AbstractZigToolchain
import com.falsepattern.zigbrains.shared.SubConfigurable
import com.intellij.openapi.Disposable
import com.intellij.openapi.extensions.ExtensionPointName
import com.intellij.openapi.project.Project
import com.intellij.ui.dsl.builder.Panel
interface ZigProjectConfigurationProvider {
fun handleMainConfigChanged(project: Project)
fun createConfigurable(project: Project): SubConfigurable
fun createNewProjectSettingsPanel(): SettingsPanel
val priority: Int
companion object {
private val EXTENSION_POINT_NAME = ExtensionPointName.create<ZigProjectConfigurationProvider>("com.falsepattern.zigbrains.projectConfigProvider")
fun mainConfigChanged(project: Project) {
EXTENSION_POINT_NAME.extensionList.forEach { it.handleMainConfigChanged(project) }
}
fun createConfigurables(project: Project): List<SubConfigurable> {
return EXTENSION_POINT_NAME.extensionList.sortedBy { it.priority }.map { it.createConfigurable(project) }
}
fun createNewProjectSettingsPanels(): List<SettingsPanel> {
return EXTENSION_POINT_NAME.extensionList.sortedBy { it.priority }.map { it.createNewProjectSettingsPanel() }
}
}
interface SettingsPanel: Disposable {
val data: Settings
fun attach(p: Panel)
}
interface Settings {
fun apply(project: Project)
}
interface ToolchainProvider {
val toolchain: AbstractZigToolchain?
}
}

View file

@ -23,6 +23,7 @@
package com.falsepattern.zigbrains.project.settings
import com.falsepattern.zigbrains.project.toolchain.LocalZigToolchain
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.io.toNioPathOrNull
import com.intellij.util.xmlb.annotations.Transient
import kotlin.io.path.pathString
@ -32,10 +33,14 @@ data class ZigProjectSettings(
var overrideStdPath: Boolean = false,
var explicitPathToStd: String? = null,
var toolchainPath: String? = null
) {
): ZigProjectConfigurationProvider.Settings, ZigProjectConfigurationProvider.ToolchainProvider {
override fun apply(project: Project) {
project.zigProjectSettings.loadState(this)
}
@get:Transient
@set:Transient
var toolchain: LocalZigToolchain?
override var toolchain: LocalZigToolchain?
get() = toolchainPath?.toNioPathOrNull()?.let { LocalZigToolchain(it) }
set(value) {
toolchainPath = value?.location?.pathString

View file

@ -52,7 +52,7 @@ import kotlinx.coroutines.launch
import javax.swing.event.DocumentEvent
import kotlin.io.path.pathString
class ZigProjectSettingsPanel(private val project: Project?) : Disposable {
class ZigProjectSettingsPanel(private val project: Project?) : ZigProjectConfigurationProvider.SettingsPanel {
private val direnv = JBCheckBox(ZigBrainsBundle.message("settings.project.label.direnv")).apply { addActionListener {
dispatchAutodetect(true)
} }
@ -107,7 +107,7 @@ class ZigProjectSettingsPanel(private val project: Project?) : Disposable {
}
}
var data
override var data
get() = ZigProjectSettings(
direnv.isSelected,
stdFieldOverride.isSelected,
@ -123,7 +123,7 @@ class ZigProjectSettingsPanel(private val project: Project?) : Disposable {
dispatchUpdateUI()
}
fun attach(p: Panel): Unit = with(p) {
override fun attach(p: Panel): Unit = with(p) {
val project = project ?: ProjectManager.getInstance().defaultProject
data = project.zigProjectSettings.state
group(ZigBrainsBundle.message("settings.project.group.title")) {

View file

@ -27,7 +27,7 @@ import com.intellij.openapi.util.Disposer
import com.intellij.ui.dsl.builder.panel
import javax.swing.JComponent
abstract class MultiConfigurable(private vararg val configurables: SubConfigurable): Configurable {
abstract class MultiConfigurable(private val configurables: List<SubConfigurable>): Configurable {
override fun createComponent(): JComponent? {
return panel {
for (configurable in configurables) {

View file

@ -184,13 +184,12 @@
</extensions>
<extensions defaultExtensionNs="com.falsepattern.zigbrains">
<!--suppress PluginXmlValidity -->
<zlsConfigProvider
implementation="com.falsepattern.zigbrains.project.toolchain.ToolchainZLSConfigProvider"
/>
<toolchainProvider
implementation="com.falsepattern.zigbrains.project.toolchain.LocalZigToolchainProvider"
/>
<projectConfigProvider
implementation="com.falsepattern.zigbrains.project.settings.ZigCoreProjectConfigurationProvider"
/>
</extensions>
<actions resource-bundle="zigbrains.ActionsBundle">

22
lsp/build.gradle.kts Normal file
View file

@ -0,0 +1,22 @@
import org.jetbrains.grammarkit.tasks.GenerateLexerTask
import org.jetbrains.grammarkit.tasks.GenerateParserTask
import org.jetbrains.intellij.platform.gradle.IntelliJPlatformType
plugins {
kotlin("plugin.serialization")
}
val lsp4ijVersion: String by project
val lsp4jVersion: String by project
val ideaCommunityVersion: String by project
val useInstaller = property("useInstaller").toString().toBoolean()
dependencies {
intellijPlatform {
create(IntelliJPlatformType.IntellijIdeaCommunity, ideaCommunityVersion, useInstaller = useInstaller)
}
compileOnly("org.jetbrains.kotlinx:kotlinx-serialization-core:1.7.3")
compileOnly("com.redhat.devtools.intellij:lsp4ij:$lsp4ijVersion")
compileOnly("org.eclipse.lsp4j:org.eclipse.lsp4j:$lsp4jVersion")
implementation(project(":core"))
}

View file

@ -0,0 +1,46 @@
/*
* This file is part of ZigBrains.
*
* Copyright (C) 2023-2025 FalsePattern
* All Rights Reserved
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* ZigBrains is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, only version 3 of the License.
*
* ZigBrains is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with ZigBrains. If not, see <https://www.gnu.org/licenses/>.
*/
package com.falsepattern.zigbrains.lsp
import com.falsepattern.zigbrains.lsp.settings.ZLSSettingsConfigurable
import com.falsepattern.zigbrains.lsp.settings.ZLSSettingsPanel
import com.falsepattern.zigbrains.project.settings.ZigProjectConfigurationProvider
import com.falsepattern.zigbrains.shared.SubConfigurable
import com.intellij.openapi.project.Project
class ZLSProjectConfigurationProvider: ZigProjectConfigurationProvider {
override fun handleMainConfigChanged(project: Project) {
startLSP(project, true)
}
override fun createConfigurable(project: Project): SubConfigurable {
return ZLSSettingsConfigurable(project)
}
override fun createNewProjectSettingsPanel(): ZigProjectConfigurationProvider.SettingsPanel {
return ZLSSettingsPanel(null)
}
override val priority: Int
get() = 1000
}

View file

@ -0,0 +1,47 @@
/*
* This file is part of ZigBrains.
*
* Copyright (C) 2023-2025 FalsePattern
* All Rights Reserved
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* ZigBrains is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, only version 3 of the License.
*
* ZigBrains is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with ZigBrains. If not, see <https://www.gnu.org/licenses/>.
*/
package com.falsepattern.zigbrains.lsp
import com.falsepattern.zigbrains.direnv.DirenvCmd
import com.falsepattern.zigbrains.direnv.emptyEnv
import com.falsepattern.zigbrains.direnv.getDirenv
import com.falsepattern.zigbrains.lsp.settings.zlsSettings
import com.intellij.openapi.project.Project
import com.intellij.openapi.startup.ProjectActivity
import kotlin.io.path.pathString
class ZLSStartup: ProjectActivity {
override suspend fun execute(project: Project) {
val zlsState = project.zlsSettings.state
if (zlsState.zlsPath.isBlank()) {
val env = if (DirenvCmd.direnvInstalled() && !project.isDefault && zlsState.direnv)
project.getDirenv()
else
emptyEnv
env.findExecutableOnPATH("zls")?.let {
zlsState.zlsPath = it.pathString
project.zlsSettings.state = zlsState
}
}
}
}

View file

@ -22,6 +22,8 @@
package com.falsepattern.zigbrains.lsp.settings
import com.falsepattern.zigbrains.project.settings.ZigProjectConfigurationProvider
import com.intellij.openapi.project.Project
import org.jetbrains.annotations.NonNls
data class ZLSSettings(
@ -36,4 +38,8 @@ data class ZLSSettings(
var comptimeInterpreter: Boolean = false,
var inlayHints: Boolean = true,
var inlayHintsCompact: Boolean = true
)
): ZigProjectConfigurationProvider.Settings {
override fun apply(project: Project) {
project.zlsSettings.loadState(this)
}
}

View file

@ -27,6 +27,7 @@ import com.falsepattern.zigbrains.direnv.Env
import com.falsepattern.zigbrains.direnv.emptyEnv
import com.falsepattern.zigbrains.direnv.getDirenv
import com.falsepattern.zigbrains.lsp.ZLSBundle
import com.falsepattern.zigbrains.project.settings.ZigProjectConfigurationProvider
import com.falsepattern.zigbrains.shared.coroutine.launchWithEDT
import com.falsepattern.zigbrains.shared.zigCoroutineScope
import com.intellij.openapi.Disposable
@ -43,7 +44,7 @@ import com.intellij.ui.dsl.builder.AlignX
import com.intellij.ui.dsl.builder.Panel
import kotlin.io.path.pathString
class ZLSSettingsPanel(private val project: Project?) : Disposable {
class ZLSSettingsPanel(private val project: Project?) : ZigProjectConfigurationProvider.SettingsPanel {
private val zlsPath = textFieldWithBrowseButton(
project,
FileChooserDescriptorFactory.createSingleFileDescriptor().withTitle(ZLSBundle.message("settings.zls-path.browse.title")),
@ -66,7 +67,7 @@ class ZLSSettingsPanel(private val project: Project?) : Disposable {
dispatchAutodetect(true)
} }
fun attach(panel: Panel) = with(panel) {
override fun attach(panel: Panel) = with(panel) {
group(ZLSBundle.message("settings.group.title")) {
row(ZLSBundle.message("settings.zls-path.label")) {
cell(zlsPath).resizableColumn().align(AlignX.FILL)
@ -89,7 +90,7 @@ class ZLSSettingsPanel(private val project: Project?) : Disposable {
dispatchAutodetect(false)
}
var data
override var data
get() = ZLSSettings(
direnv.isSelected,
zlsPath.text,

View file

@ -32,6 +32,7 @@ import com.intellij.openapi.util.UserDataHolderBase
import com.intellij.openapi.util.io.toNioPathOrNull
import kotlin.io.path.pathString
class ToolchainZLSConfigProvider: SuspendingZLSConfigProvider {
override suspend fun getEnvironment(project: Project, previous: ZLSConfig): ZLSConfig {
val svc = project.zigProjectSettings

View file

@ -1,3 +1,25 @@
<!--
~ This file is part of ZigBrains.
~
~ Copyright (C) 2023-2025 FalsePattern
~ All Rights Reserved
~
~ The above copyright notice and this permission notice shall be included
~ in all copies or substantial portions of the Software.
~
~ ZigBrains is free software: you can redistribute it and/or modify
~ it under the terms of the GNU Lesser General Public License as published by
~ the Free Software Foundation, only version 3 of the License.
~
~ ZigBrains is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU Lesser General Public License for more details.
~
~ You should have received a copy of the GNU Lesser General Public License
~ along with ZigBrains. If not, see <https://www.gnu.org/licenses/>.
-->
<idea-plugin>
<resource-bundle>zigbrains.lsp.Bundle</resource-bundle>
<extensions defaultExtensionNs="com.intellij">
@ -22,12 +44,20 @@
bundle="zigbrains.lsp.Bundle"
key="notification.group.zigbrains-lsp"
/>
<postStartupActivity
implementation="com.falsepattern.zigbrains.lsp.ZLSStartup"/>
</extensions>
<extensions defaultExtensionNs="com.falsepattern.zigbrains">
<zlsConfigProvider
implementation="com.falsepattern.zigbrains.lsp.settings.ZLSSettingsConfigProvider"
/>
<zlsConfigProvider
implementation="com.falsepattern.zigbrains.project.toolchain.ToolchainZLSConfigProvider"
/>
<projectConfigProvider
implementation="com.falsepattern.zigbrains.lsp.ZLSProjectConfigurationProvider"
/>
</extensions>
<extensions defaultExtensionNs="com.redhat.devtools.lsp4ij">

View file

@ -4,4 +4,5 @@ plugins {
rootProject.name = "ZigBrains"
include("core")
include("lsp")
include("cidr")

View file

@ -17,6 +17,11 @@
dynamic="true"
name="zlsConfigProvider"
/>
<extensionPoint
interface="com.falsepattern.zigbrains.project.settings.ZigProjectConfigurationProvider"
dynamic="true"
name="projectConfigProvider"
/>
<extensionPoint
interface="com.falsepattern.zigbrains.project.toolchain.ZigToolchainProvider"
dynamic="true"