more LSP work and some misc fixes

This commit is contained in:
FalsePattern 2024-10-31 23:05:38 +01:00
parent 5457d83296
commit d02f3859a1
Signed by: falsepattern
GPG key ID: E930CDEC50C50E23
21 changed files with 961 additions and 166 deletions

View file

@ -0,0 +1,108 @@
/*
* This file is part of ZigBrains.
*
* Copyright (C) 2023-2024 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
import com.intellij.ide.BrowserUtil
import com.intellij.ide.plugins.PluginManager
import com.intellij.notification.Notification
import com.intellij.notification.NotificationAction
import com.intellij.notification.NotificationType
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.extensions.PluginId
import com.intellij.openapi.options.Configurable
import com.intellij.openapi.options.ShowSettingsUtil
import com.intellij.openapi.project.Project
import com.intellij.openapi.startup.ProjectActivity
import com.intellij.util.application
import java.lang.reflect.Constructor
import java.lang.reflect.Method
class ZBStartup: ProjectActivity {
var firstInit = true
override suspend fun execute(project: Project) {
if (firstInit) {
firstInit = false
if (!PluginManager.isPluginInstalled(PluginId.getId("com.intellij.modules.cidr.debugger")) &&
PluginManager.isPluginInstalled(PluginId.getId("com.intellij.modules.nativeDebug-plugin-capable"))) {
val notif = Notification(
"zigbrains",
ZigBrainsBundle.message("notification.title.native-debug"),
ZigBrainsBundle.message("notification.content.native-debug"),
NotificationType.INFORMATION
)
if (JBInternalPluginManagerConfigurable.successful) {
notif.addAction(object: NotificationAction(ZigBrainsBundle.message("notification.content.native-debug.market")) {
override fun actionPerformed(e: AnActionEvent, notification: Notification) {
val configurable = JBInternalPluginManagerConfigurable()
ShowSettingsUtil.getInstance().editConfigurable(null as Project?, configurable.instance) {
configurable.openMarketplaceTab("/vendor:\"JetBrains s.r.o.\" /tag:Debugging \"Native Debugging Support\"")
}
}
})
}
notif.addAction(object: NotificationAction(ZigBrainsBundle.message("notification.content.native-debug.browser")) {
override fun actionPerformed(e: AnActionEvent, notification: Notification) {
BrowserUtil.browse("https://plugins.jetbrains.com/plugin/12775-native-debugging-support")
}
})
notif.notify(null)
}
}
}
}
//JetBrains Internal API, but we need to access it, so access it reflectively (hopefully safe enough to pass verifier)
private class JBInternalPluginManagerConfigurable {
init {
if (!successful) {
throw IllegalStateException()
}
}
val instance = constructor.newInstance() as Configurable
fun openMarketplaceTab(option: String) {
openMarketplaceTab.invoke(instance, option)
}
companion object {
private lateinit var constructor: Constructor<*>
private lateinit var openMarketplaceTab: Method
val successful: Boolean
init {
lateinit var constructor: Constructor<*>
lateinit var openMarketplaceTab: Method
val successful = try {
val theClass = Class.forName("com_intellij_ide_plugins_PluginManagerConfigurable".replace('_', '.'))
constructor = theClass.getDeclaredConstructor().apply { isAccessible = true }
openMarketplaceTab = theClass.getDeclaredMethod("openMarketplaceTab", String::class.java).apply { isAccessible = true }
true
} catch (_: Throwable) { false }
if (successful) {
this.constructor = constructor
this.openMarketplaceTab = openMarketplaceTab
}
this.successful = successful
}
}
}

View file

@ -29,20 +29,24 @@ import com.intellij.notification.Notification
import com.intellij.notification.NotificationType
import com.intellij.notification.Notifications
import com.intellij.openapi.diagnostic.logger
import com.intellij.openapi.progress.runBlockingCancellable
import com.intellij.openapi.project.Project
import com.intellij.openapi.project.guessProjectDir
import com.intellij.util.io.awaitExit
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withContext
import kotlinx.serialization.json.Json
import org.jetbrains.annotations.NonNls
import java.nio.file.Path
class DirenvCmd(private val workingDirectory: Path) {
suspend fun importDirenv(): Env {
object DirenvCmd {
suspend fun importDirenv(project: Project): Env {
if (!direnvInstalled())
return emptyEnv
val workDir = project.guessProjectDir()?.toNioPath() ?: return emptyEnv
val runOutput = run("export", "json")
val runOutput = run(project, workDir, "export", "json")
if (runOutput.error) {
if (runOutput.output.contains("is blocked")) {
Notifications.Bus.notify(Notification(
@ -50,7 +54,7 @@ class DirenvCmd(private val workingDirectory: Path) {
ZigBrainsBundle.message("notification.title.direnv-blocked"),
ZigBrainsBundle.message("notification.content.direnv-blocked"),
NotificationType.ERROR
))
))
return emptyEnv
} else {
Notifications.Bus.notify(Notification(
@ -65,34 +69,34 @@ class DirenvCmd(private val workingDirectory: Path) {
return Env(Json.decodeFromString<Map<String, String>>(runOutput.output))
}
private suspend fun run(project: Project, workDir: Path, vararg args: String): DirenvOutput {
val cli = GeneralCommandLine("direnv", *args).withWorkingDirectory(workDir)
@NonNls
private suspend fun run(vararg args: String): DirenvOutput {
@NonNls
val cli = GeneralCommandLine("direnv", *args)
.withWorkingDirectory(workingDirectory)
val process: Process
val exitCode: Int
val process = cli.createProcess()
if (process.awaitExit() != 0) {
project.direnvService.mutex.withLock {
process = cli.createProcess()
exitCode = process.awaitExit()
}
if (exitCode != 0) {
val stdErr = process.errorStream.bufferedReader().use { it.readText() }
return DirenvOutput(stdErr, true)
}
val stdOut = process.errorStream.bufferedReader().use { it.readText() }
val stdOut = process.inputStream.bufferedReader().use { it.readText() }
return DirenvOutput(stdOut, false)
}
companion object {
@NonNls
private const val GROUP_DISPLAY_ID = "zigbrains-direnv"
private val LOG = logger<DirenvCmd>()
fun direnvInstalled() =
// Using the builtin stuff here instead of Env because it should only scan for direnv on the process path
PathEnvironmentVariableUtil.findExecutableInPathOnAnyOS("direnv") != null
}
private const val GROUP_DISPLAY_ID = "zigbrains-direnv"
fun direnvInstalled() =
// Using the builtin stuff here instead of Env because it should only scan for direnv on the process path
PathEnvironmentVariableUtil.findExecutableInPathOnAnyOS("direnv") != null
}
suspend fun Project?.getDirenv(): Env {
val dir = this?.guessProjectDir() ?: return emptyEnv
return DirenvCmd(dir.toNioPath()).importDirenv()
if (this == null)
return emptyEnv
return DirenvCmd.importDirenv(this)
}

View file

@ -24,12 +24,12 @@ package com.falsepattern.zigbrains.direnv
import com.intellij.openapi.components.Service
import com.intellij.openapi.components.service
import com.intellij.util.application
import kotlinx.coroutines.CoroutineScope
@Service
class DirenvService(val cs: CoroutineScope) {
import com.intellij.openapi.project.Project
import kotlinx.coroutines.sync.Mutex
@Service(Service.Level.PROJECT)
class DirenvProjectService {
val mutex = Mutex()
}
val direnvScope get() = application.service<DirenvService>().cs
val Project.direnvService get() = service<DirenvProjectService>()

View file

@ -22,21 +22,17 @@
package com.falsepattern.zigbrains.lsp
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.project.Project
import com.redhat.devtools.lsp4ij.server.OSProcessStreamConnectionProvider
import com.intellij.util.messages.Topic
class ZigStreamConnectionProvider(private val project: Project): OSProcessStreamConnectionProvider() {
init {
}
companion object {
private val LOG = Logger.getInstance(ZigStreamConnectionProvider::class.java)
suspend fun getCommand(project: Project, full: Boolean) {
}
}
interface LanguageServerStarter {
fun startLSP(project: Project, restart: Boolean)
}
fun startLSP(project: Project, restart: Boolean) {
val publisher = project.messageBus.syncPublisher(LANGUAGE_SERVER_START_TOPIC)
publisher.startLSP(project, restart)
}
@Topic.ProjectLevel
val LANGUAGE_SERVER_START_TOPIC = Topic.create("LanguageServerStarter", LanguageServerStarter::class.java)

View file

@ -0,0 +1,112 @@
<idea-plugin>
<!-- region Zig -->
<extensions defaultExtensionNs="com.intellij">
<colorSettingsPage
implementation="com.falsepattern.zigbrains.zig.highlighter.ZigColorSettingsPage"/>
<fileType
name="Zig File"
implementationClass="com.falsepattern.zigbrains.zig.ZigFileType"
fieldName="INSTANCE"
language="Zig"
extensions="zig"/>
<lang.braceMatcher
language="Zig"
implementationClass="com.falsepattern.zigbrains.zig.pairing.ZigBraceMatcher"/>
<lang.commenter
language="Zig"
implementationClass="com.falsepattern.zigbrains.zig.comments.ZigCommenter"/>
<lang.formatter
language="Zig"
implementationClass="com.falsepattern.zigbrains.zig.formatter.ZigFormattingModelBuilder"/>
<lang.parserDefinition
language="Zig"
implementationClass="com.falsepattern.zigbrains.zig.parser.ZigParserDefinition"/>
<lang.syntaxHighlighterFactory
language="Zig"
implementationClass="com.falsepattern.zigbrains.zig.highlighter.ZigSyntaxHighlighterFactory"/>
<!-- String manipulation -->
<enterHandlerDelegate
id="ZigEnterInTextBlockHandler"
implementation="com.falsepattern.zigbrains.zig.editing.ZigEnterInTextBlockHandler"/>
<enterHandlerDelegate
id="ZigEnterInQuotedStringHandler"
implementation="com.falsepattern.zigbrains.zig.editing.ZigEnterInQuotedStringHandler"/>
<intentionAction>
<language>Zig</language>
<className>com.falsepattern.zigbrains.zig.intentions.MakeStringMultiline</className>
<category>Zig</category>
</intentionAction>
<intentionAction>
<language>Zig</language>
<className>com.falsepattern.zigbrains.zig.intentions.MakeStringQuoted</className>
<category>Zig</category>
</intentionAction>
<!-- Language injection -->
<lang.elementManipulator
forClass="com.falsepattern.zigbrains.zig.psi.ZigStringLiteral"
implementationClass="com.falsepattern.zigbrains.zig.injection.ZigStringElementManipulator"/>
<languageInjectionPerformer
language="Zig"
implementationClass="com.falsepattern.zigbrains.zig.injection.ZigLanguageInjectionPerformer"/>
</extensions>
<!-- endregion Zig -->
<!-- region Zon -->
<extensions defaultExtensionNs="com.intellij">
<colorSettingsPage
implementation="com.falsepattern.zigbrains.zon.highlighting.ZonColorSettingsPage"/>
<completion.contributor
language="Zon"
implementationClass="com.falsepattern.zigbrains.zon.completion.ZonCompletionContributor"/>
<fileType
name="Zon File"
implementationClass="com.falsepattern.zigbrains.zon.ZonFileType"
fieldName="INSTANCE"
language="Zon"
extensions="zon"/>
<lang.braceMatcher
language="Zon"
implementationClass="com.falsepattern.zigbrains.zon.pairing.ZonBraceMatcher"/>
<lang.commenter
language="Zon"
implementationClass="com.falsepattern.zigbrains.zon.comments.ZonCommenter"/>
<lang.foldingBuilder
language="Zon"
implementationClass="com.falsepattern.zigbrains.zon.folding.ZonFoldingBuilder"/>
<lang.formatter
language="Zon"
implementationClass="com.falsepattern.zigbrains.zon.formatter.ZonFormattingModelBuilder"/>
<lang.parserDefinition
language="Zon"
implementationClass="com.falsepattern.zigbrains.zon.parser.ZonParserDefinition"/>
<lang.quoteHandler
language="Zon"
implementationClass="com.falsepattern.zigbrains.zon.pairing.ZonQuoteHandler"/>
<lang.syntaxHighlighterFactory
language="Zon"
implementationClass="com.falsepattern.zigbrains.zon.highlighting.ZonSyntaxHighlighterFactory"/>
</extensions>
<!-- endregion Zon -->
<!-- region direnv -->
<extensions defaultExtensionNs="com.intellij">
<notificationGroup displayType="BALLOON"
id="zigbrains-direnv"
bundle="zigbrains.Bundle"
key="notification.group.zigbrains-direnv"/>
<notificationGroup displayType="BALLOON"
id="zigbrains"
bundle="zigbrains.Bundle"
key="notification.group.zigbrains"/>
</extensions>
<!-- endregion direnv -->
<!-- region misc -->
<extensions defaultExtensionNs="com.intellij">
<postStartupActivity
implementation="com.falsepattern.zigbrains.ZBStartup"/>
</extensions>
<!-- endregion misc -->
</idea-plugin>

View file

@ -63,3 +63,8 @@ zon.color-settings.comma=Comma
zon.color-settings.dot=Dot
zon.color-settings.boolean=Boolean
zon.color-settings.brace=Braces
notification.group.zigbrains=ZigBrains
notification.title.native-debug=Zig native debugger
notification.content.native-debug=You need to install the "Native Debugging Support" plugin for Zig debugging in this IDE!
notification.content.native-debug.market=Install from Marketplace
notification.content.native-debug.browser=Open in Browser

View file

@ -21,12 +21,17 @@
*/
import org.jetbrains.intellij.platform.gradle.IntelliJPlatformType
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
val lsp4ijVersion: String by project
val lsp4jVersion: String by project
val lsp4ijNightly = property("lsp4ijNightly").toString().toBoolean()
val lsp4ijDepString = "${if (lsp4ijNightly) "nightly." else ""}com.jetbrains.plugins:com.redhat.devtools.lsp4ij:$lsp4ijVersion"
tasks.withType<KotlinCompile> {
kotlinOptions {
freeCompilerArgs += "-Xcontext-receivers"
}
}
dependencies {
intellijPlatform {
create(IntelliJPlatformType.IntellijIdeaCommunity, providers.gradleProperty("ideaCommunityVersion"))

View file

@ -0,0 +1,231 @@
/*
* This file is part of ZigBrains.
*
* Copyright (C) 2023-2024 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.emptyEnv
import com.falsepattern.zigbrains.direnv.getDirenv
import com.falsepattern.zigbrains.lsp.config.ZLSConfigProviderBase
import com.falsepattern.zigbrains.lsp.settings.zlsSettings
import com.intellij.execution.configurations.GeneralCommandLine
import com.intellij.notification.Notification
import com.intellij.notification.NotificationType
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.project.Project
import com.intellij.openapi.project.guessProjectDir
import com.intellij.openapi.util.SystemInfo
import com.intellij.openapi.util.io.FileUtil
import com.intellij.openapi.util.io.toNioPathOrNull
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.openapi.vfs.toNioPathOrNull
import com.redhat.devtools.lsp4ij.server.OSProcessStreamConnectionProvider
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.encodeToStream
import org.eclipse.lsp4j.InlayHint
import org.eclipse.lsp4j.jsonrpc.messages.Message
import org.eclipse.lsp4j.jsonrpc.messages.ResponseMessage
import org.eclipse.lsp4j.services.LanguageServer
import java.nio.file.Path
import kotlin.io.path.isExecutable
import kotlin.io.path.isRegularFile
import kotlin.io.path.notExists
import kotlin.io.path.pathString
class ZLSStreamConnectionProvider private constructor(private val project: Project, commandLine: GeneralCommandLine?) : OSProcessStreamConnectionProvider(commandLine) {
override fun handleMessage(message: Message?, languageServer: LanguageServer?, rootUri: VirtualFile?) {
if (project.zlsSettings.state.inlayHintsCompact) {
if (message is ResponseMessage) {
val res = message.result
if (res is Collection<*>) {
res.forEach { e ->
if (e is InlayHint) {
tryMutateInlayHint(e)
}
}
} else if (res is InlayHint) {
tryMutateInlayHint(res)
}
}
}
super.handleMessage(message, languageServer, rootUri)
}
companion object {
private val LOG = Logger.getInstance(ZLSStreamConnectionProvider::class.java)
private val ERROR_BLOCK = Regex("error\\{.*?}", RegexOption.DOT_MATCHES_ALL)
suspend fun create(project: Project): ZLSStreamConnectionProvider {
val projectDir = project.guessProjectDir()?.toNioPathOrNull()
val commandLine = getCommand(project)?.let { GeneralCommandLine(it) }?.withWorkingDirectory(projectDir)
return ZLSStreamConnectionProvider(project, commandLine)
}
suspend fun validate(project: Project): Boolean {
val svc = project.zlsSettings
val state = svc.state
val zlsPath: Path = state.zlsPath.let { zlsPath ->
if (zlsPath.isEmpty()) {
val env = if (state.direnv) project.getDirenv() else emptyEnv
env.findExecutableOnPATH("zls") ?: run {
return false
}
} else {
zlsPath.toNioPathOrNull() ?: run {
return false
}
}
}
if (zlsPath.notExists()) {
return false
}
if (!zlsPath.isRegularFile() || !zlsPath.isExecutable()) {
return false
}
return true
}
@OptIn(ExperimentalSerializationApi::class)
suspend fun getCommand(project: Project): List<String>? {
val svc = project.zlsSettings
val state = svc.state
val zlsPath: Path = state.zlsPath.let { zlsPath ->
if (zlsPath.isEmpty()) {
val env = if (state.direnv) project.getDirenv() else emptyEnv
env.findExecutableOnPATH("zls") ?: run {
Notification(
"zigbrains-lsp",
ZLSBundle.message("notification.message.could-not-detect.content"),
NotificationType.ERROR
).notify(project)
return null
}
} else {
zlsPath.toNioPathOrNull() ?: run {
Notification(
"zigbrains-lsp",
ZLSBundle.message("notification.message.zls-exe-path-invalid.content", zlsPath),
NotificationType.ERROR
).notify(project)
return null
}
}
}
if (zlsPath.notExists()) {
Notification(
"zigbrains-lsp",
ZLSBundle.message("notification.message.zls-exe-not-exists.content", zlsPath),
NotificationType.ERROR
).notify(project)
return null
}
if (!zlsPath.isRegularFile() || !zlsPath.isExecutable()) {
Notification(
"zigbrains-lsp",
ZLSBundle.message("notification.message.zls-exe-not-executable.content", zlsPath),
NotificationType.ERROR
).notify(project)
return null
}
val configPath: Path? = state.zlsConfigPath.let { configPath ->
if (configPath.isNotBlank()) {
configPath.toNioPathOrNull()?.let { nioPath ->
if (nioPath.notExists()) {
Notification(
"zigbrains-lsp",
ZLSBundle.message("notification.message.zls-config-not-exists.content", nioPath),
NotificationType.ERROR
).notify(project)
null
} else if (!nioPath.isRegularFile()) {
Notification(
"zigbrains-lsp",
ZLSBundle.message("notification.message.zls-config-not-file.content", nioPath),
NotificationType.ERROR
).notify(project)
null
} else {
nioPath
}
} ?: run {
Notification(
"zigbrains-lsp",
ZLSBundle.message("notification.message.zls-config-path-invalid.content", configPath),
NotificationType.ERROR
).notify(project)
null
}
} else {
null
}
} ?: run {
val config = ZLSConfigProviderBase.findEnvironment(project)
if (config.zigExePath.isNullOrEmpty() || config.zigLibPath.isNullOrEmpty()) {
Notification(
"zigbrains-lsp",
ZLSBundle.message("notification.message.zls-config-autogen-failed.content"),
NotificationType.ERROR
).notify(project)
null
} else {
val tmpFile = FileUtil.createTempFile("zigbrains-zls-autoconf", ".json", true)
tmpFile.outputStream().buffered().use { writer -> Json.encodeToStream(config, writer) }
tmpFile.toPath()
}
}
val cmd = ArrayList<String>()
cmd.add(zlsPath.pathString)
if (configPath != null) {
cmd.add("--config-path")
cmd.add(configPath.pathString)
}
if (state.debug) {
cmd.add("--enable-debug-log")
}
if (state.messageTrace) {
cmd.add("--enable-message-tracing")
}
if (SystemInfo.isWindows) {
val sb: StringBuilder by lazy { StringBuilder() }
for (i in 0..<cmd.size) {
val s = cmd[i]
if (s.contains(' ')) {
sb.setLength(0)
cmd[i] = sb.append('"').append(s).append('"').toString()
}
}
}
return cmd
}
private fun tryMutateInlayHint(inlayHint: InlayHint) {
val str = inlayHint.label?.left ?: return
val shortened = ERROR_BLOCK.replace(str, "error{...}")
inlayHint.setLabel(shortened)
}
}
}

View file

@ -0,0 +1,36 @@
/*
* This file is part of ZigBrains.
*
* Copyright (C) 2023-2024 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.intellij.openapi.components.Service
import kotlinx.coroutines.CoroutineScope
@Service(Service.Level.PROJECT)
class ZigLSPProjectService(
val cs: CoroutineScope
)
@Service
class ZigLSPApplicationService(
val cs: CoroutineScope
)

View file

@ -22,5 +22,61 @@
package com.falsepattern.zigbrains.lsp
class ZigLanguageServerFactory {
import com.intellij.openapi.components.service
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.Key
import com.intellij.platform.ide.progress.ModalTaskOwner
import com.intellij.platform.ide.progress.runWithModalProgressBlocking
import com.intellij.util.application
import com.redhat.devtools.lsp4ij.LanguageServerEnablementSupport
import com.redhat.devtools.lsp4ij.LanguageServerFactory
import com.redhat.devtools.lsp4ij.LanguageServerManager
import com.redhat.devtools.lsp4ij.ServerStatus
import com.redhat.devtools.lsp4ij.server.StreamConnectionProvider
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
class ZigLanguageServerFactory: LanguageServerFactory, LanguageServerEnablementSupport {
override fun createConnectionProvider(project: Project): StreamConnectionProvider {
return if (application.isDispatchThread) {
runWithModalProgressBlocking(ModalTaskOwner.project(project), ZLSBundle.message("progress.title.create-connection-provider")) {
ZLSStreamConnectionProvider.create(project)
}
} else {
runBlocking {
ZLSStreamConnectionProvider.create(project)
}
}
}
override fun isEnabled(project: Project): Boolean {
return (project.getUserData(ENABLED_KEY) ?: true) && if (application.isDispatchThread) {
runWithModalProgressBlocking(ModalTaskOwner.project(project), ZLSBundle.message("progress.title.validate")) {
ZLSStreamConnectionProvider.validate(project)
}
} else {
runBlocking {
ZLSStreamConnectionProvider.validate(project)
}
}
}
override fun setEnabled(enabled: Boolean, project: Project) {
project.putUserData(ENABLED_KEY, true)
}
}
class ZLSStarter: LanguageServerStarter {
override fun startLSP(project: Project, restart: Boolean) {
project.service<ZigLSPProjectService>().cs.launch {
val manager = project.service<LanguageServerManager>()
val status = manager.getServerStatus("ZigBrains")
if ((status == ServerStatus.started || status == ServerStatus.starting) && !restart)
return@launch
manager.start("ZigBrains")
}
}
}
private val ENABLED_KEY = Key.create<Boolean>("ZLS_ENABLED")

View file

@ -34,15 +34,4 @@ data class ZLSConfig(
@SerialName("build_on_save_step") val buildOnSaveStep: @NonNls String? = null,
@SerialName("dangerous_comptime_experiments_do_not_enable") val comptimeInterpreter: Boolean? = null,
@SerialName("highlight_global_var_declarations") val globalVarDeclarations: Boolean? = null
) {
infix fun merge(other: ZLSConfig): ZLSConfig {
return ZLSConfig(
zigExePath ?: other.zigExePath,
zigLibPath ?: other.zigLibPath,
buildOnSave ?: other.buildOnSave,
buildOnSaveStep ?: other.buildOnSaveStep,
comptimeInterpreter ?: other.comptimeInterpreter,
globalVarDeclarations ?: other.globalVarDeclarations
)
}
}
)

View file

@ -25,18 +25,28 @@ package com.falsepattern.zigbrains.lsp.config
import com.intellij.openapi.extensions.ExtensionPointName
import com.intellij.openapi.project.Project
interface ZLSConfigProvider {
fun getEnvironment(project: Project): ZLSConfig
sealed interface ZLSConfigProviderBase {
companion object {
private val EXTENSION_POINT_NAME = ExtensionPointName.create<ZLSConfigProvider>("com.falsepattern.zigbrains.zlsConfigProvider")
private val EXTENSION_POINT_NAME = ExtensionPointName.create<ZLSConfigProviderBase>("com.falsepattern.zigbrains.zlsConfigProvider")
fun findEnvironment(project: Project): ZLSConfig {
suspend fun findEnvironment(project: Project): ZLSConfig {
val extensions = EXTENSION_POINT_NAME.extensionList
var result = ZLSConfig()
for (extension in extensions) {
result = result merge extension.getEnvironment(project)
result = when (extension) {
is ZLSConfigProvider -> extension.getEnvironment(project, result)
is SuspendingZLSConfigProvider -> extension.getEnvironment(project, result)
}
}
return result
}
}
}
interface ZLSConfigProvider: ZLSConfigProviderBase {
fun getEnvironment(project: Project, previous: ZLSConfig): ZLSConfig
}
interface SuspendingZLSConfigProvider: ZLSConfigProviderBase {
suspend fun getEnvironment(project: Project, previous: ZLSConfig): ZLSConfig
}

View file

@ -0,0 +1,136 @@
/*
* This file is part of ZigBrains.
*
* Copyright (C) 2023-2024 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.highlighting
import com.falsepattern.zigbrains.lsp.highlighting.ZLSSemanticTokenModifiers.Declaration
import com.falsepattern.zigbrains.lsp.highlighting.ZLSSemanticTokenModifiers.Definition
import com.falsepattern.zigbrains.lsp.highlighting.ZLSSemanticTokenModifiers.Deprecated
import com.falsepattern.zigbrains.lsp.highlighting.ZLSSemanticTokenModifiers.Generic
import com.falsepattern.zigbrains.lsp.highlighting.ZLSSemanticTokenTypes.Builtin
import com.falsepattern.zigbrains.lsp.highlighting.ZLSSemanticTokenTypes.Comment
import com.falsepattern.zigbrains.lsp.highlighting.ZLSSemanticTokenTypes.Enum
import com.falsepattern.zigbrains.lsp.highlighting.ZLSSemanticTokenTypes.EnumMember
import com.falsepattern.zigbrains.lsp.highlighting.ZLSSemanticTokenTypes.ErrorTag
import com.falsepattern.zigbrains.lsp.highlighting.ZLSSemanticTokenTypes.Function
import com.falsepattern.zigbrains.lsp.highlighting.ZLSSemanticTokenTypes.Keyword
import com.falsepattern.zigbrains.lsp.highlighting.ZLSSemanticTokenTypes.KeywordLiteral
import com.falsepattern.zigbrains.lsp.highlighting.ZLSSemanticTokenTypes.Label
import com.falsepattern.zigbrains.lsp.highlighting.ZLSSemanticTokenTypes.Method
import com.falsepattern.zigbrains.lsp.highlighting.ZLSSemanticTokenTypes.Namespace
import com.falsepattern.zigbrains.lsp.highlighting.ZLSSemanticTokenTypes.Number
import com.falsepattern.zigbrains.lsp.highlighting.ZLSSemanticTokenTypes.Operator
import com.falsepattern.zigbrains.lsp.highlighting.ZLSSemanticTokenTypes.Parameter
import com.falsepattern.zigbrains.lsp.highlighting.ZLSSemanticTokenTypes.Property
import com.falsepattern.zigbrains.lsp.highlighting.ZLSSemanticTokenTypes.String
import com.falsepattern.zigbrains.lsp.highlighting.ZLSSemanticTokenTypes.Struct
import com.falsepattern.zigbrains.lsp.highlighting.ZLSSemanticTokenTypes.Type
import com.falsepattern.zigbrains.lsp.highlighting.ZLSSemanticTokenTypes.TypeParameter
import com.falsepattern.zigbrains.lsp.highlighting.ZLSSemanticTokenTypes.Variable
import com.falsepattern.zigbrains.zig.highlighter.ZigSyntaxHighlighter.Companion.BUILTIN
import com.falsepattern.zigbrains.zig.highlighter.ZigSyntaxHighlighter.Companion.ENUM_DECL
import com.falsepattern.zigbrains.zig.highlighter.ZigSyntaxHighlighter.Companion.ENUM_MEMBER_DECL
import com.falsepattern.zigbrains.zig.highlighter.ZigSyntaxHighlighter.Companion.ENUM_MEMBER_REF
import com.falsepattern.zigbrains.zig.highlighter.ZigSyntaxHighlighter.Companion.ENUM_REF
import com.falsepattern.zigbrains.zig.highlighter.ZigSyntaxHighlighter.Companion.ERROR_TAG_DECL
import com.falsepattern.zigbrains.zig.highlighter.ZigSyntaxHighlighter.Companion.ERROR_TAG_REF
import com.falsepattern.zigbrains.zig.highlighter.ZigSyntaxHighlighter.Companion.FUNCTION_DECL
import com.falsepattern.zigbrains.zig.highlighter.ZigSyntaxHighlighter.Companion.FUNCTION_DECL_GEN
import com.falsepattern.zigbrains.zig.highlighter.ZigSyntaxHighlighter.Companion.FUNCTION_REF
import com.falsepattern.zigbrains.zig.highlighter.ZigSyntaxHighlighter.Companion.FUNCTION_REF_GEN
import com.falsepattern.zigbrains.zig.highlighter.ZigSyntaxHighlighter.Companion.KEYWORD
import com.falsepattern.zigbrains.zig.highlighter.ZigSyntaxHighlighter.Companion.LABEL_DECL
import com.falsepattern.zigbrains.zig.highlighter.ZigSyntaxHighlighter.Companion.LABEL_REF
import com.falsepattern.zigbrains.zig.highlighter.ZigSyntaxHighlighter.Companion.METHOD_DECL
import com.falsepattern.zigbrains.zig.highlighter.ZigSyntaxHighlighter.Companion.METHOD_DECL_GEN
import com.falsepattern.zigbrains.zig.highlighter.ZigSyntaxHighlighter.Companion.METHOD_REF
import com.falsepattern.zigbrains.zig.highlighter.ZigSyntaxHighlighter.Companion.METHOD_REF_GEN
import com.falsepattern.zigbrains.zig.highlighter.ZigSyntaxHighlighter.Companion.NAMESPACE_DECL
import com.falsepattern.zigbrains.zig.highlighter.ZigSyntaxHighlighter.Companion.NAMESPACE_REF
import com.falsepattern.zigbrains.zig.highlighter.ZigSyntaxHighlighter.Companion.NUMBER
import com.falsepattern.zigbrains.zig.highlighter.ZigSyntaxHighlighter.Companion.OPERATOR
import com.falsepattern.zigbrains.zig.highlighter.ZigSyntaxHighlighter.Companion.PARAMETER
import com.falsepattern.zigbrains.zig.highlighter.ZigSyntaxHighlighter.Companion.PROPERTY_DECL
import com.falsepattern.zigbrains.zig.highlighter.ZigSyntaxHighlighter.Companion.PROPERTY_REF
import com.falsepattern.zigbrains.zig.highlighter.ZigSyntaxHighlighter.Companion.STRUCT_DECL
import com.falsepattern.zigbrains.zig.highlighter.ZigSyntaxHighlighter.Companion.STRUCT_REF
import com.falsepattern.zigbrains.zig.highlighter.ZigSyntaxHighlighter.Companion.TYPE_DECL
import com.falsepattern.zigbrains.zig.highlighter.ZigSyntaxHighlighter.Companion.TYPE_DECL_GEN
import com.falsepattern.zigbrains.zig.highlighter.ZigSyntaxHighlighter.Companion.TYPE_PARAM
import com.falsepattern.zigbrains.zig.highlighter.ZigSyntaxHighlighter.Companion.TYPE_PARAM_DECL
import com.falsepattern.zigbrains.zig.highlighter.ZigSyntaxHighlighter.Companion.TYPE_REF
import com.falsepattern.zigbrains.zig.highlighter.ZigSyntaxHighlighter.Companion.TYPE_REF_GEN
import com.falsepattern.zigbrains.zig.highlighter.ZigSyntaxHighlighter.Companion.VARIABLE_DECL
import com.falsepattern.zigbrains.zig.highlighter.ZigSyntaxHighlighter.Companion.VARIABLE_DECL_DEPR
import com.falsepattern.zigbrains.zig.highlighter.ZigSyntaxHighlighter.Companion.VARIABLE_REF
import com.falsepattern.zigbrains.zig.highlighter.ZigSyntaxHighlighter.Companion.VARIABLE_REF_DEPR
import com.intellij.psi.PsiFile
import com.redhat.devtools.lsp4ij.features.semanticTokens.DefaultSemanticTokensColorsProvider
class ZLSSemanticTokenColorsProvider : DefaultSemanticTokensColorsProvider() {
override fun getTextAttributesKey(type: String, mod: List<String>, file: PsiFile) = with(mod) {
when (type) {
Builtin -> BUILTIN
Enum -> if (isDecl) ENUM_DECL else ENUM_REF
EnumMember -> if (isDecl) ENUM_MEMBER_DECL else ENUM_MEMBER_REF
ErrorTag -> if (isDecl) ERROR_TAG_DECL else ERROR_TAG_REF
Property -> if (isDecl) PROPERTY_DECL else PROPERTY_REF
Function -> if (isDecl)
if (has(Generic)) FUNCTION_DECL_GEN else FUNCTION_DECL
else
if (has(Generic)) FUNCTION_REF_GEN else FUNCTION_REF
Keyword, KeywordLiteral -> KEYWORD
Label -> if (isDecl) LABEL_DECL else LABEL_REF
Method -> if (isDecl)
if (has(Generic)) METHOD_DECL_GEN else METHOD_DECL
else
if (has(Generic)) METHOD_REF_GEN else METHOD_REF
Namespace -> if (isDecl) NAMESPACE_DECL else NAMESPACE_REF
Number -> NUMBER
Operator -> OPERATOR
Parameter -> PARAMETER
Struct -> if (isDecl) STRUCT_DECL else STRUCT_REF
Type -> if (isDecl)
if (has(Generic)) TYPE_DECL_GEN else TYPE_DECL
else
if (has(Generic)) TYPE_REF_GEN else TYPE_REF
TypeParameter -> if (isDecl) TYPE_PARAM_DECL else TYPE_PARAM
Variable -> if (isDecl)
if (has(Deprecated)) VARIABLE_DECL_DEPR else VARIABLE_DECL
else
if (has(Deprecated)) VARIABLE_REF_DEPR else VARIABLE_REF
Comment, String -> null
else -> super.getTextAttributesKey(type, mod, file)
}
}
}
private fun List<String>.has(key: String) = contains(key)
private val List<String>.isDecl get() = has(Declaration) || has(Definition)

View file

@ -0,0 +1,21 @@
package com.falsepattern.zigbrains.lsp.highlighting
import org.eclipse.lsp4j.SemanticTokenModifiers
import org.jetbrains.annotations.NonNls
@NonNls
object ZLSSemanticTokenModifiers {
const val Declaration: String = SemanticTokenModifiers.Declaration
const val Definition: String = SemanticTokenModifiers.Definition
const val Readonly: String = SemanticTokenModifiers.Readonly
const val Static: String = SemanticTokenModifiers.Static
const val Deprecated: String = SemanticTokenModifiers.Deprecated
const val Abstract: String = SemanticTokenModifiers.Abstract
const val Async: String = SemanticTokenModifiers.Async
const val Modification: String = SemanticTokenModifiers.Modification
const val Documentation: String = SemanticTokenModifiers.Documentation
const val DefaultLibrary: String = SemanticTokenModifiers.DefaultLibrary
/** non standard token modifier */
const val Generic: String = "generic"
}

View file

@ -0,0 +1,65 @@
/*
* This file is part of ZigBrains.
*
* Copyright (C) 2023-2024 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.highlighting
import org.eclipse.lsp4j.SemanticTokenTypes
import org.jetbrains.annotations.NonNls
@NonNls
object ZLSSemanticTokenTypes {
const val Namespace: String = SemanticTokenTypes.Namespace
const val Type: String = SemanticTokenTypes.Type
const val Class: String = SemanticTokenTypes.Class
const val Enum: String = SemanticTokenTypes.Enum
const val Interface: String = SemanticTokenTypes.Interface
const val Struct: String = SemanticTokenTypes.Struct
const val TypeParameter: String = SemanticTokenTypes.TypeParameter
const val Parameter: String = SemanticTokenTypes.Parameter
const val Variable: String = SemanticTokenTypes.Variable
const val Property: String = SemanticTokenTypes.Property
const val EnumMember: String = SemanticTokenTypes.EnumMember
const val Event: String = SemanticTokenTypes.Event
const val Function: String = SemanticTokenTypes.Function
const val Method: String = SemanticTokenTypes.Method
const val Macro: String = SemanticTokenTypes.Macro
const val Keyword: String = SemanticTokenTypes.Keyword
const val Modifier: String = SemanticTokenTypes.Modifier
const val Comment: String = SemanticTokenTypes.Comment
const val String: String = SemanticTokenTypes.String
const val Number: String = SemanticTokenTypes.Number
const val Regexp: String = SemanticTokenTypes.Regexp
const val Operator: String = SemanticTokenTypes.Operator
const val Decorator: String = SemanticTokenTypes.Decorator
/** non standard token type */
const val ErrorTag: String = "errorTag"
/** non standard token type */
const val Builtin: String = "builtin"
/** non standard token type */
const val Label: String = "label"
/** non standard token type */
const val KeywordLiteral: String = "keywordLiteral"
}

View file

@ -0,0 +1,39 @@
/*
* This file is part of ZigBrains.
*
* Copyright (C) 2023-2024 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.settings
import com.falsepattern.zigbrains.lsp.config.ZLSConfig
import com.falsepattern.zigbrains.lsp.config.ZLSConfigProvider
import com.intellij.openapi.project.Project
class ZLSSettingsConfigProvider: ZLSConfigProvider {
override fun getEnvironment(project: Project, previous: ZLSConfig): ZLSConfig {
val state = project.zlsSettings.state
return previous.copy(
buildOnSave = state.buildOnSave,
buildOnSaveStep = state.buildOnSaveStep,
globalVarDeclarations = state.globalVarDeclarations,
comptimeInterpreter = state.comptimeInterpreter
)
}
}

View file

@ -23,6 +23,7 @@
package com.falsepattern.zigbrains.lsp.settings
import com.falsepattern.zigbrains.lsp.ZLSBundle
import com.falsepattern.zigbrains.lsp.startLSP
import com.falsepattern.zigbrains.shared.NestedConfigurable
import com.intellij.openapi.components.service
import com.intellij.openapi.project.Project
@ -47,7 +48,7 @@ class ZLSSettingsConfigurable(private val project: Project): NestedConfigurable
val reloadZLS = settings.isModified(data)
settings.state = data
if (reloadZLS) {
TODO("Not yet implemented")
startLSP(project, true)
}
}

View file

@ -24,7 +24,10 @@ package com.falsepattern.zigbrains.lsp.settings
import com.falsepattern.zigbrains.direnv.*
import com.falsepattern.zigbrains.lsp.ZLSBundle
import com.falsepattern.zigbrains.lsp.ZigLSPApplicationService
import com.falsepattern.zigbrains.lsp.ZigLSPProjectService
import com.intellij.openapi.Disposable
import com.intellij.openapi.components.service
import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory
import com.intellij.openapi.project.Project
import com.intellij.ui.components.JBCheckBox
@ -32,6 +35,7 @@ import com.intellij.ui.components.fields.ExtendableTextField
import com.intellij.ui.components.textFieldWithBrowseButton
import com.intellij.ui.dsl.builder.AlignX
import com.intellij.ui.dsl.builder.Panel
import com.intellij.util.application
import kotlinx.coroutines.launch
import kotlin.io.path.pathString
@ -110,7 +114,7 @@ class ZLSSettingsPanel(private val project: Project?) : Disposable {
}
fun autodetect() {
direnvScope.launch {
(project?.service<ZigLSPProjectService>()?.cs ?: application.service<ZigLSPApplicationService>().cs).launch {
getDirenv().findExecutableOnPATH("zls")?.let { zlsPath.text = it.pathString }
}
}

View file

@ -0,0 +1,59 @@
<idea-plugin>
<resource-bundle>zigbrains.lsp.Bundle</resource-bundle>
<extensions defaultExtensionNs="com.intellij">
<!-- LSP textDocument/signatureHelp -->
<codeInsight.parameterInfo
language="Zig"
implementationClass="com.redhat.devtools.lsp4ij.features.signatureHelp.LSPParameterInfoHandler"
/>
<!-- LSP textDocument/folding -->
<lang.foldingBuilder
language="Zig"
implementationClass="com.redhat.devtools.lsp4ij.features.foldingRange.LSPFoldingRangeBuilder"
/>
<!-- LSP textDocument/documentSymbol -->
<lang.psiStructureViewFactory
language="Zig"
implementationClass="com.redhat.devtools.lsp4ij.features.documentSymbol.LSPDocumentSymbolStructureViewFactory"
/>
<notificationGroup
id="zigbrains-lsp"
displayType="BALLOON"
bundle="zigbrains.lsp.Bundle"
key="notification.group.zigbrains-lsp"
/>
</extensions>
<extensions defaultExtensionNs="com.falsepattern.zigbrains">
<zlsConfigProvider
implementation="com.falsepattern.zigbrains.lsp.settings.ZLSSettingsConfigProvider"
/>
</extensions>
<extensions defaultExtensionNs="com.redhat.devtools.lsp4ij">
<!--suppress PluginXmlCapitalization -->
<server
id="ZigBrains"
bundle="zigbrains.lsp.Bundle"
nameKey="lsp.zls.name"
descriptionKey="lsp.zls.description"
factoryClass="com.falsepattern.zigbrains.lsp.ZigLanguageServerFactory"
/>
<languageMapping
language="Zig"
serverId="ZigBrains"
languageId="zig"
/>
<semanticTokensColorsProvider
serverId="ZigBrains"
class="com.falsepattern.zigbrains.lsp.highlighting.ZLSSemanticTokenColorsProvider"
/>
</extensions>
<applicationListeners>
<listener
class="com.falsepattern.zigbrains.lsp.ZLSStarter"
topic="com.falsepattern.zigbrains.lsp.LanguageServerStarter"
/>
</applicationListeners>
</idea-plugin>

View file

@ -18,3 +18,18 @@ dev-settings.group.title=ZLS Developer Settings
dev-settings.debug.label=Debug log
dev-settings.message-trace.label=Message trace
configurable.name.zls.settings=ZLS Settings
notification.group.zigbrains-lsp=ZigBrains LSP Integration
notification.message.could-not-detect.content=Could not detect ZLS binary, please configure it
notification.message.zls-exe-path-invalid.content=ZLS executable path could not be parsed: {0}
notification.message.zls-exe-not-exists.content=ZLS executable does not exist: {0}
notification.message.zls-exe-not-executable.content=ZLS executable is not an executable file: {0}
notification.message.zls-config-not-exists.content=ZLS config file does not exist: {0}
notification.message.zls-config-not-file.content=ZLS config file is not a regular file: {0}
notification.message.zls-config-path-invalid.content=ZLS config path could not be parted: {0}
notification.message.zls-config-autogen-failed.content=Failed to autogenerate ZLS config from toolchain
progress.title.create-connection-provider=Creating ZLS connection provider
progress.title.validate=Validating ZLS
# suppress inspection "UnusedProperty"
lsp.zls.name=Zig Language Server
# suppress inspection "UnusedProperty"
lsp.zls.description=The <a href="https://github.com/Zigtools/ZLS">Zig Language Server</a>, via ZigBrains

View file

@ -3,111 +3,14 @@
<name>ZigBrains</name>
<vendor>FalsePattern</vendor>
<depends>com.intellij.modules.platform</depends>
<depends config-file="zigbrains-core.xml">com.intellij.modules.platform</depends>
<depends config-file="zigbrains-lsp.xml">com.redhat.devtools.lsp4ij</depends>
<!-- region Zig -->
<extensions defaultExtensionNs="com.intellij">
<colorSettingsPage
implementation="com.falsepattern.zigbrains.zig.highlighter.ZigColorSettingsPage"/>
<fileType
name="Zig File"
implementationClass="com.falsepattern.zigbrains.zig.ZigFileType"
fieldName="INSTANCE"
language="Zig"
extensions="zig"/>
<lang.braceMatcher
language="Zig"
implementationClass="com.falsepattern.zigbrains.zig.pairing.ZigBraceMatcher"/>
<lang.commenter
language="Zig"
implementationClass="com.falsepattern.zigbrains.zig.comments.ZigCommenter"/>
<lang.formatter
language="Zig"
implementationClass="com.falsepattern.zigbrains.zig.formatter.ZigFormattingModelBuilder"/>
<lang.parserDefinition
language="Zig"
implementationClass="com.falsepattern.zigbrains.zig.parser.ZigParserDefinition"/>
<lang.syntaxHighlighterFactory
language="Zig"
implementationClass="com.falsepattern.zigbrains.zig.highlighter.ZigSyntaxHighlighterFactory"/>
<!-- String manipulation -->
<enterHandlerDelegate
id="ZigEnterInTextBlockHandler"
implementation="com.falsepattern.zigbrains.zig.editing.ZigEnterInTextBlockHandler"/>
<enterHandlerDelegate
id="ZigEnterInQuotedStringHandler"
implementation="com.falsepattern.zigbrains.zig.editing.ZigEnterInQuotedStringHandler"/>
<intentionAction>
<language>Zig</language>
<className>com.falsepattern.zigbrains.zig.intentions.MakeStringMultiline</className>
<category>Zig</category>
</intentionAction>
<intentionAction>
<language>Zig</language>
<className>com.falsepattern.zigbrains.zig.intentions.MakeStringQuoted</className>
<category>Zig</category>
</intentionAction>
<!-- Language injection -->
<lang.elementManipulator
forClass="com.falsepattern.zigbrains.zig.psi.ZigStringLiteral"
implementationClass="com.falsepattern.zigbrains.zig.injection.ZigStringElementManipulator"/>
<languageInjectionPerformer
language="Zig"
implementationClass="com.falsepattern.zigbrains.zig.injection.ZigLanguageInjectionPerformer"/>
</extensions>
<!-- endregion Zig -->
<!-- region Zon -->
<extensions defaultExtensionNs="com.intellij">
<colorSettingsPage
implementation="com.falsepattern.zigbrains.zon.highlighting.ZonColorSettingsPage"/>
<completion.contributor
language="Zon"
implementationClass="com.falsepattern.zigbrains.zon.completion.ZonCompletionContributor"/>
<fileType
name="Zon File"
implementationClass="com.falsepattern.zigbrains.zon.ZonFileType"
fieldName="INSTANCE"
language="Zon"
extensions="zon"/>
<lang.braceMatcher
language="Zon"
implementationClass="com.falsepattern.zigbrains.zon.pairing.ZonBraceMatcher"/>
<lang.commenter
language="Zon"
implementationClass="com.falsepattern.zigbrains.zon.comments.ZonCommenter"/>
<lang.foldingBuilder
language="Zon"
implementationClass="com.falsepattern.zigbrains.zon.folding.ZonFoldingBuilder"/>
<lang.formatter
language="Zon"
implementationClass="com.falsepattern.zigbrains.zon.formatter.ZonFormattingModelBuilder"/>
<lang.parserDefinition
language="Zon"
implementationClass="com.falsepattern.zigbrains.zon.parser.ZonParserDefinition"/>
<lang.quoteHandler
language="Zon"
implementationClass="com.falsepattern.zigbrains.zon.pairing.ZonQuoteHandler"/>
<lang.syntaxHighlighterFactory
language="Zon"
implementationClass="com.falsepattern.zigbrains.zon.highlighting.ZonSyntaxHighlighterFactory"/>
</extensions>
<!-- endregion Zon -->
<!-- region direnv -->
<extensions defaultExtensionNs="com.intellij">
<notificationGroup displayType="BALLOON"
id="zigbrains-direnv"
bundle="zigbrains.Bundle"
key="notification.group.zigbrains-direnv"/>
</extensions>
<!-- endregion direnv -->
<resource-bundle>zigbrains.Bundle</resource-bundle>
<extensionPoints>
<extensionPoint
interface="com.falsepattern.zigbrains.lsp.config.ZLSConfigProvider"
interface="com.falsepattern.zigbrains.lsp.config.ZLSConfigProviderBase"
dynamic="true"
name="zlsConfigProvider"/>
</extensionPoints>