more LSP work and some misc fixes
This commit is contained in:
parent
5457d83296
commit
d02f3859a1
21 changed files with 961 additions and 166 deletions
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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>()
|
|
@ -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)
|
112
modules/core/src/main/resources/META-INF/zigbrains-core.xml
Normal file
112
modules/core/src/main/resources/META-INF/zigbrains-core.xml
Normal 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>
|
|
@ -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
|
||||
|
|
|
@ -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"))
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -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
|
||||
)
|
|
@ -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")
|
|
@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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)
|
|
@ -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"
|
||||
}
|
|
@ -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"
|
||||
}
|
|
@ -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
|
||||
)
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 }
|
||||
}
|
||||
}
|
||||
|
|
59
modules/lsp/src/main/resources/META-INF/zigbrains-lsp.xml
Normal file
59
modules/lsp/src/main/resources/META-INF/zigbrains-lsp.xml
Normal 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>
|
|
@ -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
|
|
@ -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>
|
||||
|
|
Loading…
Add table
Reference in a new issue