toolchain+lsp management feature complete!
This commit is contained in:
parent
281ce0ed4e
commit
8336d2bcc5
35 changed files with 756 additions and 237 deletions
6
LICENSE
6
LICENSE
|
@ -25,6 +25,11 @@ which are the property of the Zig Software Foundation.
|
|||
(https://github.com/ziglang/logo)
|
||||
These art assets are licensed under Creative Commons Attribution-ShareAlike 4.0 International (CC BY-SA 4.0).
|
||||
--------------------------------
|
||||
The art assets inside src/art/zls, and all copies of them, are derived from the Zig Language Server,
|
||||
which are the property of the zigtools organization.
|
||||
(https://github.com/zigtools/zls)
|
||||
These art assets are licensed under MIT license.
|
||||
--------------------------------
|
||||
Parts of the codebase are based on the intellij-zig plugin,
|
||||
developed by HTGAzureX1212 (https://github.com/HTGAzureX1212), licensed under the Apache 2.0 license.
|
||||
--------------------------------
|
||||
|
@ -38,3 +43,4 @@ All of the licenses listed here are available in the following files, bundled wi
|
|||
- licenses/GPL3.LICENSE
|
||||
- licenses/INTELLIJ-RUST.LICENSE
|
||||
- licenses/LGPL3.LICENSE
|
||||
- licenses/ZLS.LICENSE
|
|
@ -143,8 +143,8 @@ class DirenvService(val project: Project): SerializablePersistentStateComponent<
|
|||
|
||||
private val STATE_KEY = Key.create<DirenvState>("DIRENV_STATE")
|
||||
|
||||
fun getStateFor(data: UserDataHolder, project: Project?): DirenvState {
|
||||
return data.getUserData(STATE_KEY) ?: project?.let { getInstance(project).isEnabled } ?: DirenvState.Disabled
|
||||
fun getStateFor(data: UserDataHolder?, project: Project?): DirenvState {
|
||||
return data?.getUserData(STATE_KEY) ?: project?.let { getInstance(project).isEnabled } ?: DirenvState.Disabled
|
||||
}
|
||||
|
||||
fun setStateFor(data: UserDataHolder, state: DirenvState) {
|
||||
|
|
|
@ -25,8 +25,10 @@ package com.falsepattern.zigbrains.direnv
|
|||
import com.intellij.openapi.util.SystemInfo
|
||||
import com.intellij.openapi.util.io.toNioPathOrNull
|
||||
import com.intellij.util.EnvironmentUtil
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.firstOrNull
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.flow.flowOn
|
||||
import org.jetbrains.annotations.NonNls
|
||||
import java.io.File
|
||||
import kotlin.io.path.absolute
|
||||
|
@ -41,8 +43,6 @@ data class Env(val env: Map<String, String>) {
|
|||
private fun getVariable(name: @NonNls String) =
|
||||
env.getOrElse(name) { EnvironmentUtil.getValue(name) }
|
||||
|
||||
suspend fun findExecutableOnPATH(exe: @NonNls String) = findAllExecutablesOnPATH(exe).firstOrNull()
|
||||
|
||||
fun findAllExecutablesOnPATH(exe: @NonNls String) = flow {
|
||||
val exeName = if (SystemInfo.isWindows) "$exe.exe" else exe
|
||||
val paths = path ?: return@flow
|
||||
|
@ -55,7 +55,7 @@ data class Env(val env: Map<String, String>) {
|
|||
continue
|
||||
emit(exePath)
|
||||
}
|
||||
}
|
||||
}.flowOn(Dispatchers.IO)
|
||||
|
||||
companion object {
|
||||
val empty = Env(emptyMap())
|
||||
|
|
|
@ -44,7 +44,10 @@ abstract class DirenvEditor<T>(private val sharedState: ZigProjectConfigurationP
|
|||
it.addItemListener { e ->
|
||||
if (e.stateChange != ItemEvent.SELECTED)
|
||||
return@addItemListener
|
||||
DirenvService.setStateFor(sharedState, DirenvState.Auto)
|
||||
val item = e.item
|
||||
if (item !is DirenvState)
|
||||
return@addItemListener
|
||||
DirenvService.setStateFor(sharedState, item)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,7 +37,8 @@ import javax.swing.JComponent
|
|||
abstract class ZigToolchainConfigurable<T: ZigToolchain>(
|
||||
val uuid: UUID,
|
||||
tc: T,
|
||||
val data: ZigProjectConfigurationProvider.IUserDataBridge?
|
||||
val data: ZigProjectConfigurationProvider.IUserDataBridge?,
|
||||
val modal: Boolean
|
||||
): NamedConfigurable<UUID>() {
|
||||
var toolchain: T = tc
|
||||
set(value) {
|
||||
|
@ -46,9 +47,7 @@ abstract class ZigToolchainConfigurable<T: ZigToolchain>(
|
|||
}
|
||||
|
||||
init {
|
||||
data?.putUserData(TOOLCHAIN_KEY, Supplier{
|
||||
myViews.fold(toolchain) { tc, view -> view.apply(tc) ?: tc }
|
||||
})
|
||||
data?.putUserData(TOOLCHAIN_KEY, Supplier{toolchain})
|
||||
}
|
||||
private var myViews: List<ImmutableElementPanel<T>> = emptyList()
|
||||
|
||||
|
@ -59,13 +58,14 @@ abstract class ZigToolchainConfigurable<T: ZigToolchain>(
|
|||
if (views.isEmpty()) {
|
||||
views = ArrayList<ImmutableElementPanel<T>>()
|
||||
views.add(createPanel())
|
||||
views.addAll(createZigToolchainExtensionPanels(data))
|
||||
views.forEach { it.reset(toolchain) }
|
||||
views.addAll(createZigToolchainExtensionPanels(data, if (modal) PanelState.ModalEditor else PanelState.ListEditor))
|
||||
myViews = views
|
||||
}
|
||||
return panel {
|
||||
val p = panel {
|
||||
views.forEach { it.attach(this@panel) }
|
||||
}.withMinimumWidth(20)
|
||||
views.forEach { it.reset(toolchain) }
|
||||
return p
|
||||
}
|
||||
|
||||
override fun getEditableObject(): UUID? {
|
||||
|
@ -99,6 +99,6 @@ abstract class ZigToolchainConfigurable<T: ZigToolchain>(
|
|||
}
|
||||
|
||||
companion object {
|
||||
val TOOLCHAIN_KEY: Key<Supplier<ZigToolchain>> = Key.create("TOOLCHAIN")
|
||||
val TOOLCHAIN_KEY: Key<Supplier<ZigToolchain?>> = Key.create("TOOLCHAIN")
|
||||
}
|
||||
}
|
|
@ -25,17 +25,22 @@ package com.falsepattern.zigbrains.project.toolchain.base
|
|||
import com.falsepattern.zigbrains.project.settings.ZigProjectConfigurationProvider
|
||||
import com.falsepattern.zigbrains.project.toolchain.ui.ImmutableElementPanel
|
||||
import com.intellij.openapi.extensions.ExtensionPointName
|
||||
import com.intellij.openapi.util.UserDataHolder
|
||||
|
||||
private val EXTENSION_POINT_NAME = ExtensionPointName.create<ZigToolchainExtensionsProvider>("com.falsepattern.zigbrains.toolchainExtensionsProvider")
|
||||
|
||||
interface ZigToolchainExtensionsProvider {
|
||||
fun <T : ZigToolchain> createExtensionPanel(sharedState: ZigProjectConfigurationProvider.IUserDataBridge?): ImmutableElementPanel<T>?
|
||||
fun <T : ZigToolchain> createExtensionPanel(sharedState: ZigProjectConfigurationProvider.IUserDataBridge?, state: PanelState): ImmutableElementPanel<T>?
|
||||
val index: Int
|
||||
}
|
||||
|
||||
fun <T: ZigToolchain> createZigToolchainExtensionPanels(sharedState: ZigProjectConfigurationProvider.IUserDataBridge?): List<ImmutableElementPanel<T>> {
|
||||
fun <T: ZigToolchain> createZigToolchainExtensionPanels(sharedState: ZigProjectConfigurationProvider.IUserDataBridge?, state: PanelState): List<ImmutableElementPanel<T>> {
|
||||
return EXTENSION_POINT_NAME.extensionList.sortedBy{ it.index }.mapNotNull {
|
||||
it.createExtensionPanel(sharedState)
|
||||
it.createExtensionPanel(sharedState, state)
|
||||
}
|
||||
}
|
||||
|
||||
enum class PanelState {
|
||||
ProjectEditor,
|
||||
ListEditor,
|
||||
ModalEditor
|
||||
}
|
|
@ -47,7 +47,7 @@ internal interface ZigToolchainProvider {
|
|||
fun deserialize(data: Map<String, String>): ZigToolchain?
|
||||
fun serialize(toolchain: ZigToolchain): Map<String, String>
|
||||
fun matchesSuggestion(toolchain: ZigToolchain, suggestion: ZigToolchain): Boolean
|
||||
fun createConfigurable(uuid: UUID, toolchain: ZigToolchain, data: ZigProjectConfigurationProvider.IUserDataBridge?): ZigToolchainConfigurable<*>
|
||||
fun createConfigurable(uuid: UUID, toolchain: ZigToolchain, data: ZigProjectConfigurationProvider.IUserDataBridge?, modal: Boolean): ZigToolchainConfigurable<*>
|
||||
suspend fun suggestToolchains(project: Project?, data: UserDataHolder): Flow<ZigToolchain>
|
||||
fun render(toolchain: ZigToolchain, component: SimpleColoredComponent, isSuggestion: Boolean, isSelected: Boolean)
|
||||
}
|
||||
|
@ -64,9 +64,9 @@ fun ZigToolchain.toRef(): ZigToolchain.Ref {
|
|||
return ZigToolchain.Ref(provider.serialMarker, provider.serialize(this), this.extraData)
|
||||
}
|
||||
|
||||
fun ZigToolchain.createNamedConfigurable(uuid: UUID, data: ZigProjectConfigurationProvider.IUserDataBridge?): ZigToolchainConfigurable<*> {
|
||||
fun ZigToolchain.createNamedConfigurable(uuid: UUID, data: ZigProjectConfigurationProvider.IUserDataBridge?, modal: Boolean): ZigToolchainConfigurable<*> {
|
||||
val provider = EXTENSION_POINT_NAME.extensionList.find { it.isCompatible(this) } ?: throw IllegalStateException()
|
||||
return provider.createConfigurable(uuid, this, data)
|
||||
return provider.createConfigurable(uuid, this, data, modal)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
|
|
|
@ -32,19 +32,10 @@ import java.awt.Component
|
|||
import java.nio.file.Path
|
||||
|
||||
class LocalToolchainDownloader(component: Component) : Downloader<LocalZigToolchain, ZigVersionInfo>(component) {
|
||||
override val windowTitle: String get() = ZigBrainsBundle.message("settings.toolchain.downloader.title")
|
||||
override val versionInfoFetchTitle: @NlsContexts.ProgressTitle String get() = ZigBrainsBundle.message("settings.toolchain.downloader.progress.fetch")
|
||||
override fun downloadProgressTitle(version: ZigVersionInfo): @NlsContexts.ProgressTitle String {
|
||||
return ZigBrainsBundle.message("settings.toolchain.downloader.progress.install", version.version.rawVersion)
|
||||
}
|
||||
override fun localSelector(): LocalSelector<LocalZigToolchain> {
|
||||
return LocalToolchainSelector(component)
|
||||
}
|
||||
override suspend fun downloadVersionList(): List<ZigVersionInfo> {
|
||||
return ZigVersionInfo.downloadVersionList()
|
||||
}
|
||||
|
||||
override fun getSuggestedPath(): Path? {
|
||||
return getSuggestedLocalToolchainPath()
|
||||
}
|
||||
override val windowTitle get() = ZigBrainsBundle.message("settings.toolchain.downloader.title")
|
||||
override val versionInfoFetchTitle get() = ZigBrainsBundle.message("settings.toolchain.downloader.progress.fetch")
|
||||
override fun downloadProgressTitle(version: ZigVersionInfo) = ZigBrainsBundle.message("settings.toolchain.downloader.progress.install", version.version.rawVersion)
|
||||
override fun localSelector() = LocalToolchainSelector(component)
|
||||
override suspend fun downloadVersionList() = ZigVersionInfo.downloadVersionList()
|
||||
override fun getSuggestedPath() = getSuggestedLocalToolchainPath()
|
||||
}
|
|
@ -29,8 +29,9 @@ import java.util.UUID
|
|||
class LocalZigToolchainConfigurable(
|
||||
uuid: UUID,
|
||||
toolchain: LocalZigToolchain,
|
||||
data: ZigProjectConfigurationProvider.IUserDataBridge?
|
||||
): ZigToolchainConfigurable<LocalZigToolchain>(uuid, toolchain, data) {
|
||||
data: ZigProjectConfigurationProvider.IUserDataBridge?,
|
||||
modal: Boolean
|
||||
): ZigToolchainConfigurable<LocalZigToolchain>(uuid, toolchain, data, modal) {
|
||||
override fun createPanel() = LocalZigToolchainPanel()
|
||||
|
||||
override fun setDisplayName(name: String?) {
|
||||
|
|
|
@ -100,10 +100,10 @@ class LocalZigToolchainPanel() : ImmutableNamedElementPanelBase<LocalZigToolchai
|
|||
return toolchain.copy(location = location, std = std, name = nameFieldValue ?: "")
|
||||
}
|
||||
|
||||
override fun reset(toolchain: LocalZigToolchain) {
|
||||
nameFieldValue = toolchain.name
|
||||
this.pathToToolchain.text = toolchain.location.pathString
|
||||
val std = toolchain.std
|
||||
override fun reset(toolchain: LocalZigToolchain?) {
|
||||
nameFieldValue = toolchain?.name ?: ""
|
||||
this.pathToToolchain.text = toolchain?.location?.pathString ?: ""
|
||||
val std = toolchain?.std
|
||||
if (std != null) {
|
||||
stdFieldOverride.isSelected = true
|
||||
pathToStd.text = std.pathString
|
||||
|
|
|
@ -28,6 +28,7 @@ import com.falsepattern.zigbrains.project.settings.ZigProjectConfigurationProvid
|
|||
import com.falsepattern.zigbrains.project.toolchain.base.ZigToolchain
|
||||
import com.falsepattern.zigbrains.project.toolchain.base.ZigToolchainConfigurable
|
||||
import com.falsepattern.zigbrains.project.toolchain.base.ZigToolchainProvider
|
||||
import com.falsepattern.zigbrains.shared.ui.renderPathNameComponent
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.UserDataHolder
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
|
@ -86,10 +87,11 @@ class LocalZigToolchainProvider: ZigToolchainProvider {
|
|||
override fun createConfigurable(
|
||||
uuid: UUID,
|
||||
toolchain: ZigToolchain,
|
||||
data: ZigProjectConfigurationProvider.IUserDataBridge?
|
||||
data: ZigProjectConfigurationProvider.IUserDataBridge?,
|
||||
modal: Boolean
|
||||
): ZigToolchainConfigurable<*> {
|
||||
toolchain as LocalZigToolchain
|
||||
return LocalZigToolchainConfigurable(uuid, toolchain, data)
|
||||
return LocalZigToolchainConfigurable(uuid, toolchain, data, modal)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
|
@ -114,29 +116,8 @@ class LocalZigToolchainProvider: ZigToolchainProvider {
|
|||
override fun render(toolchain: ZigToolchain, component: SimpleColoredComponent, isSuggestion: Boolean, isSelected: Boolean) {
|
||||
toolchain as LocalZigToolchain
|
||||
val name = toolchain.name
|
||||
val path = presentDetectedPath(toolchain.location.pathString)
|
||||
val primary: String
|
||||
var secondary: String?
|
||||
val tooltip: String?
|
||||
if (isSuggestion) {
|
||||
primary = path
|
||||
secondary = name
|
||||
} else {
|
||||
primary = name ?: "Zig"
|
||||
secondary = path
|
||||
}
|
||||
if (isSelected) {
|
||||
tooltip = secondary
|
||||
secondary = null
|
||||
} else {
|
||||
tooltip = null
|
||||
}
|
||||
component.append(primary)
|
||||
if (secondary != null) {
|
||||
component.append(" ")
|
||||
component.append(secondary, SimpleTextAttributes.GRAYED_ATTRIBUTES)
|
||||
}
|
||||
component.toolTipText = tooltip
|
||||
val path = toolchain.location.pathString
|
||||
renderPathNameComponent(path, name, "Zig", component, isSuggestion, isSelected)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -173,13 +154,3 @@ private fun getWellKnown(): List<Path> {
|
|||
res.add(home.resolve(".zig"))
|
||||
return res
|
||||
}
|
||||
|
||||
private fun presentDetectedPath(home: String, maxLength: Int = 50, suffixLength: Int = 30): String {
|
||||
//for macOS, let's try removing Bundle internals
|
||||
var home = home
|
||||
home = StringUtil.trimEnd(home, "/Contents/Home") //NON-NLS
|
||||
home = StringUtil.trimEnd(home, "/Contents/MacOS") //NON-NLS
|
||||
home = FileUtil.getLocationRelativeToUserHome(home, false)
|
||||
home = StringUtil.shortenTextWithEllipsis(home, maxLength, suffixLength)
|
||||
return home
|
||||
}
|
|
@ -32,5 +32,5 @@ interface ImmutableElementPanel<T>: Disposable {
|
|||
* Returned object must be the exact same class as the provided one.
|
||||
*/
|
||||
fun apply(elem: T): T?
|
||||
fun reset(elem: T)
|
||||
fun reset(elem: T?)
|
||||
}
|
|
@ -63,7 +63,7 @@ sealed interface ZigToolchainDriver: UUIDComboBoxDriver<ZigToolchain> {
|
|||
}
|
||||
|
||||
object ForList: ZigToolchainDriver {
|
||||
override fun constructModelList(): List<ListElemIn<ZigToolchain>> {
|
||||
override suspend fun constructModelList(): List<ListElemIn<ZigToolchain>> {
|
||||
val modelList = ArrayList<ListElemIn<ZigToolchain>>()
|
||||
modelList.addAll(ListElem.fetchGroup())
|
||||
modelList.add(Separator(ZigBrainsBundle.message("settings.toolchain.model.detected.separator"), true))
|
||||
|
@ -75,12 +75,12 @@ sealed interface ZigToolchainDriver: UUIDComboBoxDriver<ZigToolchain> {
|
|||
uuid: UUID,
|
||||
elem: ZigToolchain
|
||||
): NamedConfigurable<UUID> {
|
||||
return elem.createNamedConfigurable(uuid, ZigProjectConfigurationProvider.UserDataBridge())
|
||||
return elem.createNamedConfigurable(uuid, ZigProjectConfigurationProvider.UserDataBridge(), false)
|
||||
}
|
||||
}
|
||||
|
||||
class ForSelector(val data: ZigProjectConfigurationProvider.IUserDataBridge): ZigToolchainDriver {
|
||||
override fun constructModelList(): List<ListElemIn<ZigToolchain>> {
|
||||
override suspend fun constructModelList(): List<ListElemIn<ZigToolchain>> {
|
||||
val modelList = ArrayList<ListElemIn<ZigToolchain>>()
|
||||
modelList.add(ListElem.None())
|
||||
modelList.addAll(zigToolchainList.map { it.asActual() }.sortedBy { it.instance.name })
|
||||
|
@ -95,7 +95,7 @@ sealed interface ZigToolchainDriver: UUIDComboBoxDriver<ZigToolchain> {
|
|||
uuid: UUID,
|
||||
elem: ZigToolchain
|
||||
): NamedConfigurable<UUID> {
|
||||
return elem.createNamedConfigurable(uuid, data)
|
||||
return elem.createNamedConfigurable(uuid, data, true)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -26,7 +26,11 @@ import com.falsepattern.zigbrains.ZigBrainsBundle
|
|||
import com.falsepattern.zigbrains.project.settings.ZigProjectConfigurationProvider
|
||||
import com.falsepattern.zigbrains.project.settings.ZigProjectConfigurationProvider.Companion.PROJECT_KEY
|
||||
import com.falsepattern.zigbrains.project.toolchain.ZigToolchainService
|
||||
import com.falsepattern.zigbrains.project.toolchain.base.PanelState
|
||||
import com.falsepattern.zigbrains.project.toolchain.base.ZigToolchain
|
||||
import com.falsepattern.zigbrains.project.toolchain.base.ZigToolchainConfigurable
|
||||
import com.falsepattern.zigbrains.project.toolchain.base.createZigToolchainExtensionPanels
|
||||
import com.falsepattern.zigbrains.project.toolchain.zigToolchainList
|
||||
import com.falsepattern.zigbrains.shared.SubConfigurable
|
||||
import com.falsepattern.zigbrains.shared.ui.UUIDMapSelector
|
||||
import com.falsepattern.zigbrains.shared.zigCoroutineScope
|
||||
|
@ -35,17 +39,23 @@ import com.intellij.openapi.project.ProjectManager
|
|||
import com.intellij.openapi.util.Key
|
||||
import com.intellij.ui.dsl.builder.Panel
|
||||
import kotlinx.coroutines.launch
|
||||
import java.util.UUID
|
||||
import java.util.function.Supplier
|
||||
|
||||
class ZigToolchainEditor(private val sharedState: ZigProjectConfigurationProvider.IUserDataBridge):
|
||||
UUIDMapSelector<ZigToolchain>(ZigToolchainDriver.ForSelector(sharedState)),
|
||||
SubConfigurable<Project>,
|
||||
ZigProjectConfigurationProvider.UserDataListener
|
||||
{
|
||||
private var myViews: List<ImmutableElementPanel<ZigToolchain>> = emptyList()
|
||||
init {
|
||||
sharedState.putUserData(ZigToolchainConfigurable.TOOLCHAIN_KEY, Supplier{selectedUUID?.let { zigToolchainList[it] }})
|
||||
sharedState.addUserDataChangeListener(this)
|
||||
}
|
||||
|
||||
override fun onUserDataChanged(key: Key<*>) {
|
||||
if (key == ZigToolchainConfigurable.TOOLCHAIN_KEY)
|
||||
return
|
||||
zigCoroutineScope.launch { listChanged() }
|
||||
}
|
||||
|
||||
|
@ -59,24 +69,63 @@ class ZigToolchainEditor(private val sharedState: ZigProjectConfigurationProvide
|
|||
) {
|
||||
attachComboBoxRow(this)
|
||||
}
|
||||
var views = myViews
|
||||
if (views.isEmpty()) {
|
||||
views = ArrayList<ImmutableElementPanel<ZigToolchain>>()
|
||||
views.addAll(createZigToolchainExtensionPanels(sharedState, PanelState.ProjectEditor))
|
||||
myViews = views
|
||||
}
|
||||
views.forEach { it.attach(p) }
|
||||
}
|
||||
|
||||
override fun onSelection(uuid: UUID?) {
|
||||
sharedState.putUserData(ZigToolchainConfigurable.TOOLCHAIN_KEY, Supplier{selectedUUID?.let { zigToolchainList[it] }})
|
||||
refreshViews(uuid)
|
||||
}
|
||||
|
||||
private fun refreshViews(uuid: UUID?) {
|
||||
val toolchain = uuid?.let { zigToolchainList[it] }
|
||||
myViews.forEach { it.reset(toolchain) }
|
||||
}
|
||||
|
||||
override fun isModified(context: Project): Boolean {
|
||||
return ZigToolchainService.getInstance(context).toolchainUUID != selectedUUID
|
||||
val uuid = selectedUUID
|
||||
if (ZigToolchainService.getInstance(context).toolchainUUID != selectedUUID) {
|
||||
return true
|
||||
}
|
||||
if (uuid == null)
|
||||
return false
|
||||
val tc = zigToolchainList[uuid]
|
||||
if (tc == null)
|
||||
return false
|
||||
return myViews.any { it.isModified(tc) }
|
||||
}
|
||||
|
||||
override fun apply(context: Project) {
|
||||
ZigToolchainService.getInstance(context).toolchainUUID = selectedUUID
|
||||
val uuid = selectedUUID
|
||||
ZigToolchainService.getInstance(context).toolchainUUID = uuid
|
||||
if (uuid == null)
|
||||
return
|
||||
val tc = zigToolchainList[uuid]
|
||||
if (tc == null)
|
||||
return
|
||||
val finalTc = myViews.fold(tc) { acc, view -> view.apply(acc) ?: acc }
|
||||
zigToolchainList[uuid] = finalTc
|
||||
}
|
||||
|
||||
override fun reset(context: Project?) {
|
||||
val project = context ?: ProjectManager.getInstance().defaultProject
|
||||
selectedUUID = ZigToolchainService.getInstance(project).toolchainUUID
|
||||
val svc = ZigToolchainService.getInstance(project)
|
||||
val uuid = svc.toolchainUUID
|
||||
selectedUUID = uuid
|
||||
refreshViews(uuid)
|
||||
}
|
||||
|
||||
override fun dispose() {
|
||||
super.dispose()
|
||||
sharedState.removeUserDataChangeListener(this)
|
||||
myViews.forEach { it.dispose() }
|
||||
myViews = emptyList()
|
||||
}
|
||||
|
||||
override val newProjectBeforeInitSelector get() = true
|
||||
|
|
|
@ -49,7 +49,7 @@ import javax.swing.event.DocumentEvent
|
|||
import kotlin.io.path.pathString
|
||||
|
||||
abstract class LocalSelector<T>(val component: Component) {
|
||||
suspend fun browse(preSelected: Path? = null): T? {
|
||||
suspend open fun browse(preSelected: Path? = null): T? {
|
||||
return withEDTContext(component.asContextElement()) {
|
||||
doBrowseFromDisk(preSelected)
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ import java.util.UUID
|
|||
|
||||
interface UUIDComboBoxDriver<T> {
|
||||
val theMap: UUIDMapSerializable.Converting<T, *, *>
|
||||
fun constructModelList(): List<ListElemIn<T>>
|
||||
suspend fun constructModelList(): List<ListElemIn<T>>
|
||||
fun createContext(model: ZBModel<T>): ZBContext<T>
|
||||
fun createComboBox(model: ZBModel<T>): ZBComboBox<T>
|
||||
suspend fun resolvePseudo(context: Component, elem: ListElem.Pseudo<T>): UUID?
|
||||
|
|
|
@ -25,11 +25,13 @@ package com.falsepattern.zigbrains.shared.ui
|
|||
import com.falsepattern.zigbrains.ZigBrainsBundle
|
||||
import com.falsepattern.zigbrains.shared.StorageChangeListener
|
||||
import com.falsepattern.zigbrains.shared.coroutine.asContextElement
|
||||
import com.falsepattern.zigbrains.shared.coroutine.launchWithEDT
|
||||
import com.falsepattern.zigbrains.shared.coroutine.withEDTContext
|
||||
import com.falsepattern.zigbrains.shared.zigCoroutineScope
|
||||
import com.intellij.openapi.actionSystem.AnAction
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent
|
||||
import com.intellij.openapi.actionSystem.Presentation
|
||||
import com.intellij.openapi.application.ModalityState
|
||||
import com.intellij.openapi.observable.util.whenListChanged
|
||||
import com.intellij.openapi.project.DumbAwareAction
|
||||
import com.intellij.openapi.ui.MasterDetailsComponent
|
||||
|
@ -45,6 +47,7 @@ abstract class UUIDMapEditor<T>(val driver: UUIDComboBoxDriver<T>): MasterDetail
|
|||
private var isTreeInitialized = false
|
||||
private var registered: Boolean = false
|
||||
private var selectOnNextReload: UUID? = null
|
||||
private var disposed: Boolean = false
|
||||
private val changeListener: StorageChangeListener = { this@UUIDMapEditor.listChanged() }
|
||||
|
||||
override fun createComponent(): JComponent {
|
||||
|
@ -62,6 +65,9 @@ abstract class UUIDMapEditor<T>(val driver: UUIDComboBoxDriver<T>): MasterDetail
|
|||
override fun createActions(fromPopup: Boolean): List<AnAction> {
|
||||
val add = object : DumbAwareAction({ ZigBrainsBundle.message("settings.shared.list.add-action.name") }, Presentation.NULL_STRING, IconUtil.addIcon) {
|
||||
override fun actionPerformed(e: AnActionEvent) {
|
||||
zigCoroutineScope.launchWithEDT(ModalityState.current()) {
|
||||
if (disposed)
|
||||
return@launchWithEDT
|
||||
val modelList = driver.constructModelList()
|
||||
val model = ZBModel(modelList)
|
||||
val context = driver.createContext(model)
|
||||
|
@ -72,6 +78,7 @@ abstract class UUIDMapEditor<T>(val driver: UUIDComboBoxDriver<T>): MasterDetail
|
|||
popup.showInBestPositionFor(e.dataContext)
|
||||
}
|
||||
}
|
||||
}
|
||||
return listOf(add, MyDeleteAction())
|
||||
}
|
||||
|
||||
|
@ -86,6 +93,8 @@ abstract class UUIDMapEditor<T>(val driver: UUIDComboBoxDriver<T>): MasterDetail
|
|||
if (elem !is ListElem.Pseudo)
|
||||
return
|
||||
zigCoroutineScope.launch(myWholePanel.asContextElement()) {
|
||||
if (disposed)
|
||||
return@launch
|
||||
val uuid = driver.resolvePseudo(myWholePanel, elem)
|
||||
if (uuid != null) {
|
||||
withEDTContext(myWholePanel.asContextElement()) {
|
||||
|
@ -103,6 +112,7 @@ abstract class UUIDMapEditor<T>(val driver: UUIDComboBoxDriver<T>): MasterDetail
|
|||
override fun getEmptySelectionString() = ZigBrainsBundle.message("settings.shared.list.empty")
|
||||
|
||||
override fun disposeUIResources() {
|
||||
disposed = true
|
||||
super.disposeUIResources()
|
||||
if (registered) {
|
||||
driver.theMap.removeChangeListener(changeListener)
|
||||
|
@ -115,8 +125,12 @@ abstract class UUIDMapEditor<T>(val driver: UUIDComboBoxDriver<T>): MasterDetail
|
|||
}
|
||||
|
||||
private fun reloadTree() {
|
||||
if (disposed)
|
||||
return
|
||||
val currentSelection = selectedObject?.asSafely<UUID>()
|
||||
selectedNode = null
|
||||
myRoot.removeAllChildren()
|
||||
(myTree.model as DefaultTreeModel).reload()
|
||||
val onReload = selectOnNextReload
|
||||
selectOnNextReload = null
|
||||
var hasOnReload = false
|
||||
|
@ -128,12 +142,10 @@ abstract class UUIDMapEditor<T>(val driver: UUIDComboBoxDriver<T>): MasterDetail
|
|||
}
|
||||
(myTree.model as DefaultTreeModel).reload()
|
||||
if (hasOnReload) {
|
||||
selectNodeInTree(onReload)
|
||||
selectedNode = findNodeByObject(myRoot, onReload)
|
||||
return
|
||||
}
|
||||
currentSelection?.let {
|
||||
selectNodeInTree(it)
|
||||
}
|
||||
selectedNode = currentSelection?.let { findNodeByObject(myRoot, it) }
|
||||
}
|
||||
|
||||
@RequiresEdt
|
||||
|
@ -148,6 +160,8 @@ abstract class UUIDMapEditor<T>(val driver: UUIDComboBoxDriver<T>): MasterDetail
|
|||
}
|
||||
|
||||
private suspend fun listChanged() {
|
||||
if (disposed)
|
||||
return
|
||||
withEDTContext(myWholePanel.asContextElement()) {
|
||||
reloadTree()
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@ import com.falsepattern.zigbrains.shared.ui.ListElem
|
|||
import com.falsepattern.zigbrains.shared.zigCoroutineScope
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.openapi.application.EDT
|
||||
import com.intellij.openapi.application.ModalityState
|
||||
import com.intellij.openapi.application.runInEdt
|
||||
import com.intellij.openapi.observable.util.whenListChanged
|
||||
import com.intellij.openapi.options.ShowSettingsUtil
|
||||
|
@ -40,6 +41,7 @@ import com.intellij.ui.dsl.builder.AlignX
|
|||
import com.intellij.ui.dsl.builder.Row
|
||||
import com.intellij.util.concurrency.annotations.RequiresEdt
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Runnable
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
@ -54,7 +56,7 @@ abstract class UUIDMapSelector<T>(val driver: UUIDComboBoxDriver<T>): Disposable
|
|||
private var editButton: JButton? = null
|
||||
private val changeListener: StorageChangeListener = { this@UUIDMapSelector.listChanged() }
|
||||
init {
|
||||
model = ZBModel(driver.constructModelList())
|
||||
model = ZBModel(emptyList())
|
||||
comboBox = driver.createComboBox(model)
|
||||
comboBox.addItemListener(::itemStateChanged)
|
||||
driver.theMap.addChangeListener(changeListener)
|
||||
|
@ -67,19 +69,26 @@ abstract class UUIDMapSelector<T>(val driver: UUIDComboBoxDriver<T>): Disposable
|
|||
comboBox.isPopupVisible = true
|
||||
}
|
||||
}
|
||||
zigCoroutineScope.launchWithEDT(ModalityState.any()) {
|
||||
model.updateContents(driver.constructModelList())
|
||||
}
|
||||
}
|
||||
|
||||
protected var selectedUUID: UUID?
|
||||
get() = comboBox.selectedUUID
|
||||
set(value) {
|
||||
runInEdt {
|
||||
zigCoroutineScope.launchWithEDT(ModalityState.any()) {
|
||||
applyUUIDNowOrOnReload(value)
|
||||
}
|
||||
}
|
||||
|
||||
protected open fun onSelection(uuid: UUID?) {}
|
||||
|
||||
private fun refreshButtonState(item: ListElem<*>) {
|
||||
editButton?.isEnabled = item is ListElem.One.Actual<*>
|
||||
val actual = item is ListElem.One.Actual<*>
|
||||
editButton?.isEnabled = actual
|
||||
editButton?.repaint()
|
||||
onSelection(if (actual) item.uuid else null)
|
||||
}
|
||||
|
||||
private fun itemStateChanged(event: ItemEvent) {
|
||||
|
@ -106,6 +115,12 @@ abstract class UUIDMapSelector<T>(val driver: UUIDComboBoxDriver<T>): Disposable
|
|||
@RequiresEdt
|
||||
private fun tryReloadSelection() {
|
||||
val list = model.toList()
|
||||
if (list.size == 1) {
|
||||
comboBox.selectedItem = list[0]
|
||||
comboBox.isEnabled = false
|
||||
return
|
||||
}
|
||||
comboBox.isEnabled = true
|
||||
val onReload = selectOnNextReload
|
||||
selectOnNextReload = null
|
||||
if (onReload != null) {
|
||||
|
@ -116,13 +131,13 @@ abstract class UUIDMapSelector<T>(val driver: UUIDComboBoxDriver<T>): Disposable
|
|||
if (element == null) {
|
||||
selectOnNextReload = onReload
|
||||
} else {
|
||||
model.selectedItem = element
|
||||
comboBox.selectedItem = element
|
||||
return
|
||||
}
|
||||
}
|
||||
val selected = model.selected
|
||||
if (selected != null && list.contains(selected)) {
|
||||
model.selectedItem = selected
|
||||
comboBox.selectedItem = selected
|
||||
return
|
||||
}
|
||||
if (selected is ListElem.One.Actual<*>) {
|
||||
|
@ -131,10 +146,10 @@ abstract class UUIDMapSelector<T>(val driver: UUIDComboBoxDriver<T>): Disposable
|
|||
is ListElem.One.Actual -> it.uuid == uuid
|
||||
else -> false
|
||||
} }
|
||||
model.selectedItem = element
|
||||
comboBox.selectedItem = element
|
||||
return
|
||||
}
|
||||
model.selectedItem = ListElem.None<Any>()
|
||||
comboBox.selectedItem = ListElem.None<Any>()
|
||||
}
|
||||
|
||||
protected suspend fun listChanged() {
|
||||
|
|
|
@ -29,11 +29,14 @@ import com.intellij.openapi.application.asContextElement
|
|||
import com.intellij.openapi.application.runInEdt
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.ui.ComboBox
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import com.intellij.openapi.util.text.StringUtil
|
||||
import com.intellij.ui.CellRendererPanel
|
||||
import com.intellij.ui.CollectionComboBoxModel
|
||||
import com.intellij.ui.ColoredListCellRenderer
|
||||
import com.intellij.ui.GroupHeaderSeparator
|
||||
import com.intellij.ui.SimpleColoredComponent
|
||||
import com.intellij.ui.SimpleTextAttributes
|
||||
import com.intellij.ui.components.panels.OpaquePanel
|
||||
import com.intellij.ui.popup.list.ComboBoxPopup
|
||||
import com.intellij.util.concurrency.annotations.RequiresEdt
|
||||
|
@ -50,6 +53,7 @@ import java.util.function.Consumer
|
|||
import javax.accessibility.AccessibleContext
|
||||
import javax.swing.JList
|
||||
import javax.swing.border.Border
|
||||
import kotlin.io.path.pathString
|
||||
|
||||
class ZBComboBoxPopup<T>(
|
||||
context: ZBContext<T>,
|
||||
|
@ -65,7 +69,7 @@ open class ZBComboBox<T>(model: ZBModel<T>, renderer: (() -> ZBModel<T>)-> ZBCel
|
|||
var selectedUUID: UUID?
|
||||
set(value) {
|
||||
if (value == null) {
|
||||
selectedItem = ListElem.None
|
||||
selectedItem = ListElem.None<Any>()
|
||||
return
|
||||
}
|
||||
for (i in 0..<model.size) {
|
||||
|
@ -77,7 +81,7 @@ open class ZBComboBox<T>(model: ZBModel<T>, renderer: (() -> ZBModel<T>)-> ZBCel
|
|||
}
|
||||
}
|
||||
}
|
||||
selectedItem = ListElem.None
|
||||
selectedItem = ListElem.None<Any>()
|
||||
}
|
||||
get() {
|
||||
val item = selectedItem
|
||||
|
@ -252,4 +256,40 @@ abstract class ZBCellRenderer<T>(val getModel: () -> ZBModel<T>) : ColoredListCe
|
|||
)
|
||||
}
|
||||
|
||||
fun renderPathNameComponent(path: String, name: String?, nameFallback: String, component: SimpleColoredComponent, isSuggestion: Boolean, isSelected: Boolean) {
|
||||
val path = presentDetectedPath(path)
|
||||
val primary: String
|
||||
var secondary: String?
|
||||
val tooltip: String?
|
||||
if (isSuggestion) {
|
||||
primary = path
|
||||
secondary = name
|
||||
} else {
|
||||
primary = name ?: nameFallback
|
||||
secondary = path
|
||||
}
|
||||
if (isSelected) {
|
||||
tooltip = secondary
|
||||
secondary = null
|
||||
} else {
|
||||
tooltip = null
|
||||
}
|
||||
component.append(primary)
|
||||
if (secondary != null) {
|
||||
component.append(" ")
|
||||
component.append(secondary, SimpleTextAttributes.GRAYED_ATTRIBUTES)
|
||||
}
|
||||
component.toolTipText = tooltip
|
||||
}
|
||||
|
||||
fun presentDetectedPath(home: String, maxLength: Int = 50, suffixLength: Int = 30): String {
|
||||
//for macOS, let's try removing Bundle internals
|
||||
var home = home
|
||||
home = StringUtil.trimEnd(home, "/Contents/Home") //NON-NLS
|
||||
home = StringUtil.trimEnd(home, "/Contents/MacOS") //NON-NLS
|
||||
home = FileUtil.getLocationRelativeToUserHome(home, false)
|
||||
home = StringUtil.shortenTextWithEllipsis(home, maxLength, suffixLength)
|
||||
return home
|
||||
}
|
||||
|
||||
private val EMPTY_ICON = EmptyIcon.create(1, 16)
|
21
licenses/ZLS.LICENSE
Normal file
21
licenses/ZLS.LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) ZLS contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* This file is part of ZigBrains.
|
||||
*
|
||||
* Copyright (C) 2023-2025 FalsePattern
|
||||
* All Rights Reserved
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* ZigBrains is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, only version 3 of the License.
|
||||
*
|
||||
* ZigBrains is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with ZigBrains. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.falsepattern.zigbrains.lsp
|
||||
|
||||
import com.intellij.openapi.util.IconLoader
|
||||
import org.jetbrains.annotations.NonNls
|
||||
|
||||
@NonNls
|
||||
object LSPIcons {
|
||||
@JvmField
|
||||
val ZLS = IconLoader.getIcon("/icons/zls.svg", LSPIcons::class.java)
|
||||
}
|
|
@ -0,0 +1,236 @@
|
|||
/*
|
||||
* This file is part of ZigBrains.
|
||||
*
|
||||
* Copyright (C) 2023-2025 FalsePattern
|
||||
* All Rights Reserved
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* ZigBrains is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, only version 3 of the License.
|
||||
*
|
||||
* ZigBrains is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with ZigBrains. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.falsepattern.zigbrains.lsp.settings
|
||||
|
||||
import com.falsepattern.zigbrains.lsp.ZLSBundle
|
||||
import com.falsepattern.zigbrains.lsp.config.SemanticTokens
|
||||
import com.falsepattern.zigbrains.project.toolchain.ui.ImmutableElementPanel
|
||||
import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory
|
||||
import com.intellij.openapi.ui.ComboBox
|
||||
import com.intellij.openapi.util.Disposer
|
||||
import com.intellij.ui.components.JBCheckBox
|
||||
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.ui.dsl.builder.Row
|
||||
import org.jetbrains.annotations.PropertyKey
|
||||
|
||||
@Suppress("PrivatePropertyName")
|
||||
class ZLSSettingsPanel() : ImmutableElementPanel<ZLSSettings> {
|
||||
private val zlsConfigPath = textFieldWithBrowseButton(
|
||||
null,
|
||||
FileChooserDescriptorFactory.createSingleFileNoJarsDescriptor()
|
||||
.withTitle(ZLSBundle.message("settings.zls-config-path.browse.title"))
|
||||
).also { Disposer.register(this, it) }
|
||||
private val inlayHints = JBCheckBox()
|
||||
private val enable_snippets = JBCheckBox()
|
||||
private val enable_argument_placeholders = JBCheckBox()
|
||||
private val completion_label_details = JBCheckBox()
|
||||
private val enable_build_on_save = JBCheckBox()
|
||||
private val build_on_save_args = ExtendableTextField()
|
||||
private val semantic_tokens = ComboBox(SemanticTokens.entries.toTypedArray())
|
||||
private val inlay_hints_show_variable_type_hints = JBCheckBox()
|
||||
private val inlay_hints_show_struct_literal_field_type = JBCheckBox()
|
||||
private val inlay_hints_show_parameter_name = JBCheckBox()
|
||||
private val inlay_hints_show_builtin = JBCheckBox()
|
||||
private val inlay_hints_exclude_single_argument = JBCheckBox()
|
||||
private val inlay_hints_hide_redundant_param_names = JBCheckBox()
|
||||
private val inlay_hints_hide_redundant_param_names_last_token = JBCheckBox()
|
||||
private val warn_style = JBCheckBox()
|
||||
private val highlight_global_var_declarations = JBCheckBox()
|
||||
private val skip_std_references = JBCheckBox()
|
||||
private val prefer_ast_check_as_child_process = JBCheckBox()
|
||||
private val builtin_path = ExtendableTextField()
|
||||
private val build_runner_path = ExtendableTextField()
|
||||
private val global_cache_path = ExtendableTextField()
|
||||
|
||||
override fun attach(p: Panel): Unit = with(p) {
|
||||
fancyRow(
|
||||
"settings.zls-config-path.label",
|
||||
"settings.zls-config-path.tooltip"
|
||||
) { cell(zlsConfigPath).align(AlignX.FILL) }
|
||||
fancyRow(
|
||||
"settings.enable_snippets.label",
|
||||
"settings.enable_snippets.tooltip"
|
||||
) { cell(enable_snippets) }
|
||||
fancyRow(
|
||||
"settings.enable_argument_placeholders.label",
|
||||
"settings.enable_argument_placeholders.tooltip"
|
||||
) { cell(enable_argument_placeholders) }
|
||||
fancyRow(
|
||||
"settings.completion_label_details.label",
|
||||
"settings.completion_label_details.tooltip"
|
||||
) { cell(completion_label_details) }
|
||||
fancyRow(
|
||||
"settings.enable_build_on_save.label",
|
||||
"settings.enable_build_on_save.tooltip"
|
||||
) { cell(enable_build_on_save) }
|
||||
fancyRow(
|
||||
"settings.build_on_save_args.label",
|
||||
"settings.build_on_save_args.tooltip"
|
||||
) { cell(build_on_save_args).resizableColumn().align(AlignX.FILL) }
|
||||
fancyRow(
|
||||
"settings.semantic_tokens.label",
|
||||
"settings.semantic_tokens.tooltip"
|
||||
) { cell(semantic_tokens) }
|
||||
collapsibleGroup(ZLSBundle.message("settings.inlay-hints-group.label"), indent = false) {
|
||||
fancyRow(
|
||||
"settings.inlay-hints-enable.label",
|
||||
"settings.inlay-hints-enable.tooltip"
|
||||
) { cell(inlayHints) }
|
||||
fancyRow(
|
||||
"settings.inlay_hints_show_variable_type_hints.label",
|
||||
"settings.inlay_hints_show_variable_type_hints.tooltip"
|
||||
) { cell(inlay_hints_show_variable_type_hints) }
|
||||
fancyRow(
|
||||
"settings.inlay_hints_show_struct_literal_field_type.label",
|
||||
"settings.inlay_hints_show_struct_literal_field_type.tooltip"
|
||||
) { cell(inlay_hints_show_struct_literal_field_type) }
|
||||
fancyRow(
|
||||
"settings.inlay_hints_show_parameter_name.label",
|
||||
"settings.inlay_hints_show_parameter_name.tooltip"
|
||||
) { cell(inlay_hints_show_parameter_name) }
|
||||
fancyRow(
|
||||
"settings.inlay_hints_show_builtin.label",
|
||||
"settings.inlay_hints_show_builtin.tooltip"
|
||||
) { cell(inlay_hints_show_builtin) }
|
||||
fancyRow(
|
||||
"settings.inlay_hints_exclude_single_argument.label",
|
||||
"settings.inlay_hints_exclude_single_argument.tooltip"
|
||||
) { cell(inlay_hints_exclude_single_argument) }
|
||||
fancyRow(
|
||||
"settings.inlay_hints_hide_redundant_param_names.label",
|
||||
"settings.inlay_hints_hide_redundant_param_names.tooltip"
|
||||
) { cell(inlay_hints_hide_redundant_param_names) }
|
||||
fancyRow(
|
||||
"settings.inlay_hints_hide_redundant_param_names_last_token.label",
|
||||
"settings.inlay_hints_hide_redundant_param_names_last_token.tooltip"
|
||||
) { cell(inlay_hints_hide_redundant_param_names_last_token) }
|
||||
}
|
||||
fancyRow(
|
||||
"settings.warn_style.label",
|
||||
"settings.warn_style.tooltip"
|
||||
) { cell(warn_style) }
|
||||
fancyRow(
|
||||
"settings.highlight_global_var_declarations.label",
|
||||
"settings.highlight_global_var_declarations.tooltip"
|
||||
) { cell(highlight_global_var_declarations) }
|
||||
fancyRow(
|
||||
"settings.skip_std_references.label",
|
||||
"settings.skip_std_references.tooltip"
|
||||
) { cell(skip_std_references) }
|
||||
fancyRow(
|
||||
"settings.prefer_ast_check_as_child_process.label",
|
||||
"settings.prefer_ast_check_as_child_process.tooltip"
|
||||
) { cell(prefer_ast_check_as_child_process) }
|
||||
fancyRow(
|
||||
"settings.builtin_path.label",
|
||||
"settings.builtin_path.tooltip"
|
||||
) { cell(builtin_path).resizableColumn().align(AlignX.FILL) }
|
||||
fancyRow(
|
||||
"settings.build_runner_path.label",
|
||||
"settings.build_runner_path.tooltip"
|
||||
) { cell(build_runner_path).resizableColumn().align(AlignX.FILL) }
|
||||
fancyRow(
|
||||
"settings.global_cache_path.label",
|
||||
"settings.global_cache_path.tooltip"
|
||||
) { cell(global_cache_path).resizableColumn().align(AlignX.FILL) }
|
||||
}
|
||||
|
||||
override fun isModified(elem: ZLSSettings): Boolean {
|
||||
return elem != data
|
||||
}
|
||||
|
||||
override fun apply(elem: ZLSSettings): ZLSSettings? {
|
||||
return data
|
||||
}
|
||||
|
||||
override fun reset(elem: ZLSSettings?) {
|
||||
data = elem ?: ZLSSettings()
|
||||
}
|
||||
|
||||
private var data
|
||||
get() = ZLSSettings(
|
||||
zlsConfigPath.text,
|
||||
inlayHints.isSelected,
|
||||
enable_snippets.isSelected,
|
||||
enable_argument_placeholders.isSelected,
|
||||
completion_label_details.isSelected,
|
||||
enable_build_on_save.isSelected,
|
||||
build_on_save_args.text,
|
||||
semantic_tokens.item ?: SemanticTokens.full,
|
||||
inlay_hints_show_variable_type_hints.isSelected,
|
||||
inlay_hints_show_struct_literal_field_type.isSelected,
|
||||
inlay_hints_show_parameter_name.isSelected,
|
||||
inlay_hints_show_builtin.isSelected,
|
||||
inlay_hints_exclude_single_argument.isSelected,
|
||||
inlay_hints_hide_redundant_param_names.isSelected,
|
||||
inlay_hints_hide_redundant_param_names_last_token.isSelected,
|
||||
warn_style.isSelected,
|
||||
highlight_global_var_declarations.isSelected,
|
||||
skip_std_references.isSelected,
|
||||
prefer_ast_check_as_child_process.isSelected,
|
||||
builtin_path.text?.ifBlank { null },
|
||||
build_runner_path.text?.ifBlank { null },
|
||||
global_cache_path.text?.ifBlank { null },
|
||||
)
|
||||
set(value) {
|
||||
zlsConfigPath.text = value.zlsConfigPath
|
||||
inlayHints.isSelected = value.inlayHints
|
||||
enable_snippets.isSelected = value.enable_snippets
|
||||
enable_argument_placeholders.isSelected = value.enable_argument_placeholders
|
||||
completion_label_details.isSelected = value.completion_label_details
|
||||
enable_build_on_save.isSelected = value.enable_build_on_save
|
||||
build_on_save_args.text = value.build_on_save_args
|
||||
semantic_tokens.item = value.semantic_tokens
|
||||
inlay_hints_show_variable_type_hints.isSelected = value.inlay_hints_show_variable_type_hints
|
||||
inlay_hints_show_struct_literal_field_type.isSelected = value.inlay_hints_show_struct_literal_field_type
|
||||
inlay_hints_show_parameter_name.isSelected = value.inlay_hints_show_parameter_name
|
||||
inlay_hints_show_builtin.isSelected = value.inlay_hints_show_builtin
|
||||
inlay_hints_exclude_single_argument.isSelected = value.inlay_hints_exclude_single_argument
|
||||
inlay_hints_hide_redundant_param_names.isSelected = value.inlay_hints_hide_redundant_param_names
|
||||
inlay_hints_hide_redundant_param_names_last_token.isSelected =
|
||||
value.inlay_hints_hide_redundant_param_names_last_token
|
||||
warn_style.isSelected = value.warn_style
|
||||
highlight_global_var_declarations.isSelected = value.highlight_global_var_declarations
|
||||
skip_std_references.isSelected = value.skip_std_references
|
||||
prefer_ast_check_as_child_process.isSelected = value.prefer_ast_check_as_child_process
|
||||
builtin_path.text = value.builtin_path ?: ""
|
||||
build_runner_path.text = value.build_runner_path ?: ""
|
||||
global_cache_path.text = value.global_cache_path ?: ""
|
||||
}
|
||||
|
||||
override fun dispose() {
|
||||
zlsConfigPath.dispose()
|
||||
}
|
||||
}
|
||||
|
||||
private fun Panel.fancyRow(
|
||||
label: @PropertyKey(resourceBundle = "zigbrains.lsp.Bundle") String,
|
||||
tooltip: @PropertyKey(resourceBundle = "zigbrains.lsp.Bundle") String,
|
||||
cb: Row.() -> Unit
|
||||
) = row(ZLSBundle.message(label)) {
|
||||
contextHelp(ZLSBundle.message(tooltip))
|
||||
cb()
|
||||
}
|
|
@ -26,6 +26,7 @@ import com.intellij.openapi.ui.NamedConfigurable
|
|||
import com.intellij.openapi.util.NlsContexts
|
||||
import com.intellij.openapi.util.NlsSafe
|
||||
import com.intellij.ui.dsl.builder.panel
|
||||
import java.awt.Dimension
|
||||
import java.util.UUID
|
||||
import javax.swing.JComponent
|
||||
|
||||
|
@ -56,9 +57,11 @@ class ZLSConfigurable(val uuid: UUID, zls: ZLSVersion): NamedConfigurable<UUID>(
|
|||
view.reset(zls)
|
||||
myView = view
|
||||
}
|
||||
return panel {
|
||||
val p = panel {
|
||||
view.attach(this@panel)
|
||||
}.withMaximumWidth(20)
|
||||
}
|
||||
p.preferredSize = Dimension(640, 480)
|
||||
return p
|
||||
}
|
||||
|
||||
override fun getDisplayName(): @NlsContexts.ConfigurableName String? {
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
|
||||
package com.falsepattern.zigbrains.lsp.zls
|
||||
|
||||
import com.falsepattern.zigbrains.lsp.settings.ZLSSettingsPanel
|
||||
import com.falsepattern.zigbrains.project.toolchain.local.LocalZigToolchain
|
||||
import com.falsepattern.zigbrains.project.toolchain.ui.ImmutableNamedElementPanelBase
|
||||
import com.falsepattern.zigbrains.shared.cli.call
|
||||
|
@ -58,6 +59,7 @@ class ZLSPanel() : ImmutableNamedElementPanelBase<ZLSVersion>() {
|
|||
Disposer.register(this, it)
|
||||
}
|
||||
private val zlsVersion = JBTextArea().also { it.isEditable = false }
|
||||
private var settingsPanel: ZLSSettingsPanel? = null
|
||||
private var debounce: Job? = null
|
||||
|
||||
override fun attach(p: Panel): Unit = with(p) {
|
||||
|
@ -68,22 +70,28 @@ class ZLSPanel() : ImmutableNamedElementPanelBase<ZLSVersion>() {
|
|||
row("Version:") {
|
||||
cell(zlsVersion)
|
||||
}
|
||||
val sp = ZLSSettingsPanel()
|
||||
p.collapsibleGroup("Settings", indent = false) {
|
||||
sp.attach(this@collapsibleGroup)
|
||||
}
|
||||
settingsPanel = sp
|
||||
}
|
||||
|
||||
override fun isModified(version: ZLSVersion): Boolean {
|
||||
val name = nameFieldValue ?: return false
|
||||
val path = this.pathToZLS.text.ifBlank { null }?.toNioPathOrNull() ?: return false
|
||||
return name != version.name || version.path != path
|
||||
return name != version.name || version.path != path || settingsPanel?.isModified(version.settings) == true
|
||||
}
|
||||
|
||||
override fun apply(version: ZLSVersion): ZLSVersion? {
|
||||
val path = this.pathToZLS.text.ifBlank { null }?.toNioPathOrNull() ?: return null
|
||||
return version.copy(path = path, name = nameFieldValue ?: "")
|
||||
return version.copy(path = path, name = nameFieldValue ?: "", settings = settingsPanel?.apply(version.settings) ?: version.settings)
|
||||
}
|
||||
|
||||
override fun reset(version: ZLSVersion) {
|
||||
nameFieldValue = version.name
|
||||
this.pathToZLS.text = version.path.pathString
|
||||
override fun reset(version: ZLSVersion?) {
|
||||
nameFieldValue = version?.name ?: ""
|
||||
this.pathToZLS.text = version?.path?.pathString ?: ""
|
||||
settingsPanel?.reset(version?.settings)
|
||||
dispatchUpdateUI()
|
||||
}
|
||||
|
||||
|
@ -127,5 +135,7 @@ class ZLSPanel() : ImmutableNamedElementPanelBase<ZLSVersion>() {
|
|||
|
||||
override fun dispose() {
|
||||
debounce?.cancel("Disposed")
|
||||
settingsPanel?.dispose()
|
||||
settingsPanel = null
|
||||
}
|
||||
}
|
|
@ -63,10 +63,6 @@ data class ZLSVersion(val path: Path, override val name: String? = null, val set
|
|||
|
||||
companion object {
|
||||
suspend fun tryFromPath(path: Path): ZLSVersion? {
|
||||
if (path.isDirectory()) {
|
||||
val exeName = if (SystemInfo.isWindows) "zls.exe" else "zls"
|
||||
return tryFromPath(path.resolve(exeName))
|
||||
}
|
||||
var zls = ZLSVersion(path)
|
||||
if (!zls.isValid())
|
||||
return null
|
||||
|
|
|
@ -23,70 +23,26 @@
|
|||
package com.falsepattern.zigbrains.lsp.zls.downloader
|
||||
|
||||
import com.falsepattern.zigbrains.lsp.zls.ZLSVersion
|
||||
import com.falsepattern.zigbrains.lsp.zls.ui.getSuggestedZLSPath
|
||||
import com.falsepattern.zigbrains.project.settings.ZigProjectConfigurationProvider
|
||||
import com.falsepattern.zigbrains.project.settings.ZigProjectConfigurationProvider.IUserDataBridge
|
||||
import com.falsepattern.zigbrains.project.toolchain.base.ZigToolchainConfigurable
|
||||
import com.falsepattern.zigbrains.shared.downloader.Downloader
|
||||
import com.falsepattern.zigbrains.shared.downloader.LocalSelector
|
||||
import com.intellij.openapi.util.NlsContexts
|
||||
import com.intellij.openapi.util.io.toNioPathOrNull
|
||||
import com.intellij.util.system.OS
|
||||
import java.awt.Component
|
||||
import java.nio.file.Path
|
||||
import kotlin.io.path.isDirectory
|
||||
|
||||
class ZLSDownloader(component: Component, private val data: ZigProjectConfigurationProvider.IUserDataBridge?) : Downloader<ZLSVersion, ZLSVersionInfo>(component) {
|
||||
override val windowTitle: String
|
||||
get() = "Install ZLS"
|
||||
override val versionInfoFetchTitle: @NlsContexts.ProgressTitle String
|
||||
get() = "Fetching zls version information"
|
||||
|
||||
override fun downloadProgressTitle(version: ZLSVersionInfo): @NlsContexts.ProgressTitle String {
|
||||
return "Installing ZLS ${version.version.rawVersion}"
|
||||
}
|
||||
|
||||
override fun localSelector(): LocalSelector<ZLSVersion> {
|
||||
return ZLSLocalSelector(component)
|
||||
}
|
||||
|
||||
class ZLSDownloader(component: Component, private val data: IUserDataBridge?) : Downloader<ZLSVersion, ZLSVersionInfo>(component) {
|
||||
override val windowTitle get() = "Install ZLS"
|
||||
override val versionInfoFetchTitle get() = "Fetching zls version information"
|
||||
override fun downloadProgressTitle(version: ZLSVersionInfo) = "Installing ZLS ${version.version.rawVersion}"
|
||||
override fun localSelector() = ZLSLocalSelector(component)
|
||||
override suspend fun downloadVersionList(): List<ZLSVersionInfo> {
|
||||
val toolchain = data?.getUserData(ZigToolchainConfigurable.TOOLCHAIN_KEY)?.get() ?: return emptyList()
|
||||
val project = data.getUserData(ZigProjectConfigurationProvider.PROJECT_KEY)
|
||||
val toolchain = data?.getUserData(ZigToolchainConfigurable.TOOLCHAIN_KEY)?.get()
|
||||
val project = data?.getUserData(ZigProjectConfigurationProvider.PROJECT_KEY)
|
||||
return ZLSVersionInfo.downloadVersionInfoFor(toolchain, project)
|
||||
}
|
||||
|
||||
override fun getSuggestedPath(): Path? {
|
||||
return getSuggestedZLSPath()
|
||||
}
|
||||
}
|
||||
|
||||
fun getSuggestedZLSPath(): Path? {
|
||||
return getWellKnownZLS().getOrNull(0)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the paths to the following list of folders:
|
||||
*
|
||||
* 1. DATA/zls
|
||||
* 2. HOME/.zig
|
||||
*
|
||||
* Where DATA is:
|
||||
* - ~/Library on macOS
|
||||
* - %LOCALAPPDATA% on Windows
|
||||
* - $XDG_DATA_HOME (or ~/.local/share if not set) on other OSes
|
||||
*
|
||||
* and HOME is the user home path
|
||||
*/
|
||||
private fun getWellKnownZLS(): List<Path> {
|
||||
val home = System.getProperty("user.home")?.toNioPathOrNull() ?: return emptyList()
|
||||
val xdgDataHome = when(OS.CURRENT) {
|
||||
OS.macOS -> home.resolve("Library")
|
||||
OS.Windows -> System.getenv("LOCALAPPDATA")?.toNioPathOrNull()
|
||||
else -> System.getenv("XDG_DATA_HOME")?.toNioPathOrNull() ?: home.resolve(Path.of(".local", "share"))
|
||||
}
|
||||
val res = ArrayList<Path>()
|
||||
if (xdgDataHome != null && xdgDataHome.isDirectory()) {
|
||||
res.add(xdgDataHome.resolve("zls"))
|
||||
}
|
||||
res.add(home.resolve(".zls"))
|
||||
return res
|
||||
override fun getSuggestedPath() = getSuggestedZLSPath()
|
||||
}
|
|
@ -29,8 +29,10 @@ import com.falsepattern.zigbrains.shared.withUniqueName
|
|||
import com.intellij.icons.AllIcons
|
||||
import com.intellij.openapi.fileChooser.FileChooserDescriptor
|
||||
import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory
|
||||
import com.intellij.openapi.util.SystemInfo
|
||||
import java.awt.Component
|
||||
import java.nio.file.Path
|
||||
import kotlin.io.path.isDirectory
|
||||
|
||||
class ZLSLocalSelector(component: Component) : LocalSelector<ZLSVersion>(component) {
|
||||
override val windowTitle: String
|
||||
|
@ -38,6 +40,13 @@ class ZLSLocalSelector(component: Component) : LocalSelector<ZLSVersion>(compone
|
|||
override val descriptor: FileChooserDescriptor
|
||||
get() = FileChooserDescriptorFactory.createSingleFileNoJarsDescriptor().withTitle("ZLS binary")
|
||||
|
||||
override suspend fun browse(preSelected: Path?): ZLSVersion? {
|
||||
if (preSelected?.isDirectory() == true) {
|
||||
return super.browse(preSelected.resolve(if (SystemInfo.isWindows) "zls.exe" else "zls"))
|
||||
}
|
||||
return super.browse(preSelected)
|
||||
}
|
||||
|
||||
override suspend fun verify(path: Path): VerifyResult {
|
||||
var zls = resolve(path, null)
|
||||
var result: VerifyResult
|
||||
|
|
|
@ -53,12 +53,17 @@ data class ZLSVersionInfo(
|
|||
): VersionInfo {
|
||||
companion object {
|
||||
@OptIn(ExperimentalSerializationApi::class)
|
||||
suspend fun downloadVersionInfoFor(toolchain: ZigToolchain, project: Project?): List<ZLSVersionInfo> {
|
||||
suspend fun downloadVersionInfoFor(toolchain: ZigToolchain?, project: Project?): List<ZLSVersionInfo> {
|
||||
return withContext(Dispatchers.IO) {
|
||||
val zigVersion = toolchain.zig.getEnv(project).getOrNull()?.version ?: return@withContext emptyList()
|
||||
val single = toolchain != null
|
||||
val url = if (single) {
|
||||
getToolchainURL(toolchain, project) ?: return@withContext emptyList()
|
||||
} else {
|
||||
multiURL
|
||||
}
|
||||
val service = DownloadableFileService.getInstance()
|
||||
val tempFile = FileUtil.createTempFile(tempPluginDir, "zls_version_info", ".json", false, false)
|
||||
val desc = service.createFileDescription("https://releases.zigtools.org/v1/zls/select-version?zig_version=${URLEncoder.encode(zigVersion, Charsets.UTF_8)}&compatibility=only-runtime", tempFile.name)
|
||||
val desc = service.createFileDescription(url, tempFile.name)
|
||||
val downloader = service.createDownloader(listOf(desc), "ZLS version information")
|
||||
val downloadResults = coroutineToIndicator {
|
||||
downloader.download(tempPluginDir)
|
||||
|
@ -68,13 +73,27 @@ data class ZLSVersionInfo(
|
|||
val index = downloadResults[0].first
|
||||
val info = index.inputStream().use { Json.decodeFromStream<JsonObject>(it) }
|
||||
index.delete()
|
||||
return@withContext listOfNotNull(parseVersion(info))
|
||||
return@withContext if (single) {
|
||||
listOfNotNull(parseVersion(null, info))
|
||||
} else {
|
||||
info.mapNotNull { (key, value) -> parseVersion(key, value) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
private fun parseVersion(data: JsonObject): ZLSVersionInfo? {
|
||||
val versionTag = data["version"]?.asSafely<JsonPrimitive>()?.content
|
||||
|
||||
private suspend fun getToolchainURL(toolchain: ZigToolchain, project: Project?): String? {
|
||||
val zigVersion = toolchain.zig.getEnv(project).getOrNull()?.version ?: return null
|
||||
return "https://releases.zigtools.org/v1/zls/select-version?zig_version=${URLEncoder.encode(zigVersion, Charsets.UTF_8)}&compatibility=only-runtime"
|
||||
}
|
||||
private const val multiURL: String = "https://builds.zigtools.org/index.json"
|
||||
private fun parseVersion(versionKey: String?, data: JsonElement): ZLSVersionInfo? {
|
||||
if (data !is JsonObject) {
|
||||
return null
|
||||
}
|
||||
|
||||
val versionTag = data["version"]?.asSafely<JsonPrimitive>()?.content ?: versionKey
|
||||
|
||||
val version = SemVer.parseFromText(versionTag) ?: return null
|
||||
val date = data["date"]?.asSafely<JsonPrimitive>()?.content ?: ""
|
||||
|
|
|
@ -22,13 +22,16 @@
|
|||
|
||||
package com.falsepattern.zigbrains.lsp.zls.ui
|
||||
|
||||
import com.falsepattern.zigbrains.direnv.DirenvService
|
||||
import com.falsepattern.zigbrains.direnv.Env
|
||||
import com.falsepattern.zigbrains.lsp.ZLSBundle
|
||||
import com.falsepattern.zigbrains.lsp.zls.ZLSConfigurable
|
||||
import com.falsepattern.zigbrains.lsp.zls.ZLSVersion
|
||||
import com.falsepattern.zigbrains.lsp.zls.downloader.ZLSDownloader
|
||||
import com.falsepattern.zigbrains.lsp.zls.downloader.ZLSLocalSelector
|
||||
import com.falsepattern.zigbrains.lsp.zls.zlsInstallations
|
||||
import com.falsepattern.zigbrains.project.settings.ZigProjectConfigurationProvider
|
||||
import com.falsepattern.zigbrains.project.toolchain.base.ZigToolchainConfigurable
|
||||
import com.falsepattern.zigbrains.project.toolchain.base.ZigToolchainConfigurable.Companion.TOOLCHAIN_KEY
|
||||
import com.falsepattern.zigbrains.shared.UUIDMapSerializable
|
||||
import com.falsepattern.zigbrains.shared.ui.ListElem
|
||||
import com.falsepattern.zigbrains.shared.ui.ListElem.One.Actual
|
||||
|
@ -39,15 +42,25 @@ import com.falsepattern.zigbrains.shared.ui.ZBComboBox
|
|||
import com.falsepattern.zigbrains.shared.ui.ZBContext
|
||||
import com.falsepattern.zigbrains.shared.ui.ZBModel
|
||||
import com.falsepattern.zigbrains.shared.ui.asPending
|
||||
import com.falsepattern.zigbrains.shared.zigCoroutineScope
|
||||
import com.falsepattern.zigbrains.shared.withUniqueName
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.ui.NamedConfigurable
|
||||
import com.intellij.openapi.util.SystemInfo
|
||||
import com.intellij.openapi.util.io.toNioPathOrNull
|
||||
import com.intellij.util.system.OS
|
||||
import com.intellij.util.text.SemVer
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.FlowCollector
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.flow.flowOn
|
||||
import java.awt.Component
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
import java.util.UUID
|
||||
import kotlin.io.path.isDirectory
|
||||
import kotlin.io.path.isExecutable
|
||||
import kotlin.io.path.isRegularFile
|
||||
|
||||
sealed interface ZLSDriver: UUIDComboBoxDriver<ZLSVersion> {
|
||||
override val theMap: UUIDMapSerializable.Converting<ZLSVersion, *, *>
|
||||
|
@ -70,58 +83,146 @@ sealed interface ZLSDriver: UUIDComboBoxDriver<ZLSVersion> {
|
|||
elem: ListElem.Pseudo<ZLSVersion>
|
||||
): UUID? {
|
||||
return when(elem) {
|
||||
is ListElem.FromDisk<*> -> ZLSLocalSelector(context).browse()
|
||||
else -> null
|
||||
is ListElem.One.Suggested -> zlsInstallations.withUniqueName(elem.instance)
|
||||
is ListElem.FromDisk -> ZLSLocalSelector(context).browse()
|
||||
is ListElem.Download -> ZLSDownloader(context, data).download()
|
||||
}?.let { zlsInstallations.registerNew(it) }
|
||||
}
|
||||
|
||||
object ForList: ZLSDriver {
|
||||
override fun constructModelList(): List<ListElemIn<ZLSVersion>> {
|
||||
return listOf(ListElem.None(), Separator("", true), ListElem.FromDisk())
|
||||
}
|
||||
}
|
||||
val data: ZigProjectConfigurationProvider.IUserDataBridge?
|
||||
|
||||
@JvmRecord
|
||||
data class ForSelector(private val data: ZigProjectConfigurationProvider.IUserDataBridge?): ZLSDriver {
|
||||
override fun constructModelList(): List<ListElemIn<ZLSVersion>> {
|
||||
object ForList: ZLSDriver {
|
||||
override suspend fun constructModelList(): List<ListElemIn<ZLSVersion>> {
|
||||
val res = ArrayList<ListElemIn<ZLSVersion>>()
|
||||
res.add(ListElem.None())
|
||||
res.add(compatibleInstallations().asPending())
|
||||
res.add(Separator("", true))
|
||||
res.addAll(ListElem.fetchGroup())
|
||||
res.add(Separator(ZLSBundle.message("settings.model.detected.separator"), true))
|
||||
res.add(suggestZLSVersions().asPending())
|
||||
return res
|
||||
}
|
||||
|
||||
override suspend fun resolvePseudo(
|
||||
context: Component,
|
||||
elem: ListElem.Pseudo<ZLSVersion>
|
||||
): UUID? {
|
||||
return when(elem) {
|
||||
is ListElem.FromDisk<*> -> ZLSLocalSelector(context).browse()
|
||||
is ListElem.Download<*> -> ZLSDownloader(context, data).download()
|
||||
else -> null
|
||||
}?.let { zlsInstallations.registerNew(it) }
|
||||
override val data: ZigProjectConfigurationProvider.IUserDataBridge?
|
||||
get() = null
|
||||
}
|
||||
private fun compatibleInstallations(): Flow<Actual<ZLSVersion>> = flow {
|
||||
|
||||
@JvmRecord
|
||||
data class ForSelector(override val data: ZigProjectConfigurationProvider.IUserDataBridge?): ZLSDriver {
|
||||
override suspend fun constructModelList(): List<ListElemIn<ZLSVersion>> {
|
||||
val (project, toolchainVersion) = unpack(data)
|
||||
if (toolchainVersion == null) {
|
||||
return listOf(ListElem.None())
|
||||
}
|
||||
val res = ArrayList<ListElemIn<ZLSVersion>>()
|
||||
res.add(ListElem.None())
|
||||
res.addAll(compatibleInstallations(toolchainVersion))
|
||||
res.add(Separator("", true))
|
||||
res.addAll(ListElem.fetchGroup())
|
||||
res.add(Separator(ZLSBundle.message("settings.model.detected.separator"), true))
|
||||
res.add(suggestZLSVersions(project, data, toolchainVersion).asPending())
|
||||
return res
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun unpack(data: ZigProjectConfigurationProvider.IUserDataBridge?): Pair<Project?, SemVer?> {
|
||||
val toolchain = data?.getUserData(TOOLCHAIN_KEY)?.get()
|
||||
val project = data?.getUserData(ZigProjectConfigurationProvider.PROJECT_KEY)
|
||||
val toolchainVersion = data?.getUserData(ZigToolchainConfigurable.TOOLCHAIN_KEY)
|
||||
?.get()
|
||||
val toolchainVersion = toolchain
|
||||
?.zig
|
||||
?.getEnv(project)
|
||||
?.getOrNull()
|
||||
?.version
|
||||
?.let { SemVer.parseFromText(it) }
|
||||
?: return@flow
|
||||
zlsInstallations.forEach { (uuid, version) ->
|
||||
val zlsVersion = version.version() ?: return@forEach
|
||||
if (numericVersionEquals(toolchainVersion, zlsVersion)) {
|
||||
emit(Actual(uuid, version))
|
||||
return project to toolchainVersion
|
||||
}
|
||||
|
||||
private fun suggestZLSVersions(project: Project? = null, data: ZigProjectConfigurationProvider.IUserDataBridge? = null, toolchainVersion: SemVer? = null): Flow<ZLSVersion> = flow {
|
||||
val env = if (project != null && DirenvService.getStateFor(data, project).isEnabled(project)) {
|
||||
DirenvService.getInstance(project).import()
|
||||
} else {
|
||||
Env.empty
|
||||
}
|
||||
val existing = zlsInstallations.map { (_, zls) -> zls }
|
||||
env.findAllExecutablesOnPATH("zls").collect { path ->
|
||||
if (existing.any { it.path == path }) {
|
||||
return@collect
|
||||
}
|
||||
emitIfCompatible(path, toolchainVersion)
|
||||
}
|
||||
val exe = if (SystemInfo.isWindows) "zls.exe" else "zls"
|
||||
getWellKnownZLS().forEach { wellKnown ->
|
||||
runCatching {
|
||||
Files.newDirectoryStream(wellKnown).use { stream ->
|
||||
stream.asSequence().filterNotNull().forEach { dir ->
|
||||
val path = dir.resolve(exe)
|
||||
if (!path.isRegularFile() || !path.isExecutable()) {
|
||||
return@forEach
|
||||
}
|
||||
if (existing.any { it.path == path }) {
|
||||
return@forEach
|
||||
}
|
||||
emitIfCompatible(path, toolchainVersion)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}.flowOn(Dispatchers.IO)
|
||||
|
||||
private suspend fun FlowCollector<ZLSVersion>.emitIfCompatible(path: Path, toolchainVersion: SemVer?) {
|
||||
val ver = ZLSVersion.tryFromPath(path) ?: return
|
||||
if (isCompatible(ver, toolchainVersion)) {
|
||||
emit(ver)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun compatibleInstallations(toolchainVersion: SemVer): List<Actual<ZLSVersion>> {
|
||||
return zlsInstallations.mapNotNull { (uuid, version) ->
|
||||
if (!isCompatible(version, toolchainVersion)) {
|
||||
return@mapNotNull null
|
||||
}
|
||||
Actual(uuid, version)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun isCompatible(version: ZLSVersion, toolchainVersion: SemVer?): Boolean {
|
||||
if (toolchainVersion == null)
|
||||
return true
|
||||
val zlsVersion = version.version() ?: return false
|
||||
return numericVersionEquals(zlsVersion, toolchainVersion)
|
||||
}
|
||||
|
||||
private fun numericVersionEquals(a: SemVer, b: SemVer): Boolean {
|
||||
return a.major == b.major && a.minor == b.minor && a.patch == b.patch
|
||||
}
|
||||
|
||||
|
||||
fun getSuggestedZLSPath(): Path? {
|
||||
return getWellKnownZLS().getOrNull(0)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the paths to the following list of folders:
|
||||
*
|
||||
* 1. DATA/zls
|
||||
* 2. HOME/.zig
|
||||
*
|
||||
* Where DATA is:
|
||||
* - ~/Library on macOS
|
||||
* - %LOCALAPPDATA% on Windows
|
||||
* - $XDG_DATA_HOME (or ~/.local/share if not set) on other OSes
|
||||
*
|
||||
* and HOME is the user home path
|
||||
*/
|
||||
private fun getWellKnownZLS(): List<Path> {
|
||||
val home = System.getProperty("user.home")?.toNioPathOrNull() ?: return emptyList()
|
||||
val xdgDataHome = when(OS.CURRENT) {
|
||||
OS.macOS -> home.resolve("Library")
|
||||
OS.Windows -> System.getenv("LOCALAPPDATA")?.toNioPathOrNull()
|
||||
else -> System.getenv("XDG_DATA_HOME")?.toNioPathOrNull() ?: home.resolve(Path.of(".local", "share"))
|
||||
}
|
||||
val res = ArrayList<Path>()
|
||||
if (xdgDataHome != null && xdgDataHome.isDirectory()) {
|
||||
res.add(xdgDataHome.resolve("zls"))
|
||||
}
|
||||
res.add(home.resolve(".zls"))
|
||||
return res
|
||||
}
|
|
@ -28,6 +28,7 @@ import com.falsepattern.zigbrains.lsp.zls.ZLSVersion
|
|||
import com.falsepattern.zigbrains.lsp.zls.withZLS
|
||||
import com.falsepattern.zigbrains.lsp.zls.zlsUUID
|
||||
import com.falsepattern.zigbrains.project.settings.ZigProjectConfigurationProvider
|
||||
import com.falsepattern.zigbrains.project.toolchain.base.PanelState
|
||||
import com.falsepattern.zigbrains.project.toolchain.base.ZigToolchain
|
||||
import com.falsepattern.zigbrains.project.toolchain.base.ZigToolchainExtensionsProvider
|
||||
import com.falsepattern.zigbrains.project.toolchain.ui.ImmutableElementPanel
|
||||
|
@ -51,7 +52,7 @@ class ZLSEditor<T: ZigToolchain>(private val sharedState: ZigProjectConfiguratio
|
|||
}
|
||||
|
||||
override fun attach(panel: Panel): Unit = with(panel) {
|
||||
row("ZLS") {
|
||||
row("Language Server") {
|
||||
attachComboBoxRow(this)
|
||||
}
|
||||
}
|
||||
|
@ -64,8 +65,12 @@ class ZLSEditor<T: ZigToolchain>(private val sharedState: ZigProjectConfiguratio
|
|||
return toolchain.withZLS(selectedUUID)
|
||||
}
|
||||
|
||||
override fun reset(toolchain: T) {
|
||||
selectedUUID = toolchain.zlsUUID
|
||||
override fun reset(toolchain: T?) {
|
||||
selectedUUID = toolchain?.zlsUUID
|
||||
zigCoroutineScope.launch {
|
||||
listChanged()
|
||||
selectedUUID = toolchain?.zlsUUID
|
||||
}
|
||||
}
|
||||
|
||||
override fun dispose() {
|
||||
|
@ -74,7 +79,10 @@ class ZLSEditor<T: ZigToolchain>(private val sharedState: ZigProjectConfiguratio
|
|||
}
|
||||
|
||||
class Provider: ZigToolchainExtensionsProvider {
|
||||
override fun <T : ZigToolchain> createExtensionPanel(sharedState: ZigProjectConfigurationProvider.IUserDataBridge?): ImmutableElementPanel<T>? {
|
||||
override fun <T : ZigToolchain> createExtensionPanel(sharedState: ZigProjectConfigurationProvider.IUserDataBridge?, state: PanelState): ImmutableElementPanel<T>? {
|
||||
if (state == PanelState.ModalEditor) {
|
||||
return null
|
||||
}
|
||||
return ZLSEditor(sharedState)
|
||||
}
|
||||
|
||||
|
|
|
@ -25,7 +25,6 @@ package com.falsepattern.zigbrains.lsp.zls.ui
|
|||
import com.falsepattern.zigbrains.lsp.ZLSBundle
|
||||
import com.falsepattern.zigbrains.lsp.zls.ZLSVersion
|
||||
import com.falsepattern.zigbrains.shared.ui.UUIDMapEditor
|
||||
import com.intellij.openapi.ui.MasterDetailsComponent
|
||||
import com.intellij.openapi.util.NlsContexts
|
||||
|
||||
class ZLSListEditor : UUIDMapEditor<ZLSVersion>(ZLSDriver.ForList) {
|
||||
|
|
|
@ -22,15 +22,15 @@
|
|||
|
||||
package com.falsepattern.zigbrains.lsp.zls.ui
|
||||
|
||||
import com.falsepattern.zigbrains.Icons
|
||||
import com.falsepattern.zigbrains.lsp.LSPIcons
|
||||
import com.falsepattern.zigbrains.lsp.ZLSBundle
|
||||
import com.falsepattern.zigbrains.lsp.zls.ZLSVersion
|
||||
import com.falsepattern.zigbrains.project.toolchain.base.render
|
||||
import com.falsepattern.zigbrains.shared.ui.ListElem
|
||||
import com.falsepattern.zigbrains.shared.ui.ZBCellRenderer
|
||||
import com.falsepattern.zigbrains.shared.ui.ZBComboBox
|
||||
import com.falsepattern.zigbrains.shared.ui.ZBContext
|
||||
import com.falsepattern.zigbrains.shared.ui.ZBModel
|
||||
import com.falsepattern.zigbrains.shared.ui.renderPathNameComponent
|
||||
import com.intellij.icons.AllIcons
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.ui.SimpleTextAttributes
|
||||
|
@ -56,17 +56,13 @@ class ZLSCellRenderer(getModel: () -> ZBModel<ZLSVersion>): ZBCellRenderer<ZLSVe
|
|||
is ListElem.One -> {
|
||||
val (icon, isSuggestion) = when(value) {
|
||||
is ListElem.One.Suggested -> AllIcons.General.Information to true
|
||||
is ListElem.One.Actual -> Icons.Zig to false
|
||||
is ListElem.One.Actual -> LSPIcons.ZLS to false
|
||||
}
|
||||
this.icon = icon
|
||||
val item = value.instance
|
||||
//TODO proper renderer
|
||||
if (item.name != null) {
|
||||
append(item.name)
|
||||
append(item.path.pathString, SimpleTextAttributes.GRAYED_ATTRIBUTES)
|
||||
} else {
|
||||
append(item.path.pathString)
|
||||
}
|
||||
val name = item.name
|
||||
val path = item.path.pathString
|
||||
renderPathNameComponent(path, name, "ZLS", this, isSuggestion, index == -1)
|
||||
}
|
||||
|
||||
is ListElem.Download -> {
|
||||
|
|
19
lsp/src/main/resources/icons/zls.svg
Normal file
19
lsp/src/main/resources/icons/zls.svg
Normal file
|
@ -0,0 +1,19 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg version="1.1" viewBox="0 0 125.88 74.012" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
|
||||
<metadata>
|
||||
<rdf:RDF>
|
||||
<cc:Work rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
|
||||
<dc:title/>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g transform="scale(1 2) translate(-4.3455 -50.664)">
|
||||
<g transform="translate(67.635 -26.611)" fill="#f7a41d" stroke-width="1.1227" aria-label="ZLS">
|
||||
<path d="m-62.391 113.61q-0.35928-0.9431-0.58383-2.4251-0.22455-1.5269-0.26946-3.1437 0-1.6617 0.08982-3.1437 0.13473-1.5269 0.4491-2.47 1.3024-0.494 2.8742-1.1227t3.2335-1.976q1.6617-1.3922 3.1886-3.997 1.5718-2.6497 2.7395-7.0957l-1.3473-0.26946q-0.80838 3.0539-1.976 5.3892-1.1677 2.3353-3.1886 3.7724-1.976 1.4371-5.2544 1.8862-0.4491-2.8742-0.67365-6.0628-0.17964-3.2335-0.17964-6.2425 0.04491-3.009 0.22455-5.2994 0.22455-2.3353 0.49401-3.4131 3.8173 0.13473 7.2754 0.26946 3.4581 0.08982 6.9161 0.17964 3.503 0.04491 7.2754 0.04491 2.6048 0 5.5688-0.04491 3.009-0.04491 5.8383-0.08982 2.8742-0.08982 5.0748-0.13473 2.2006-0.08982 3.2335-0.17964 0.4491 1.0778 0.67365 2.6497 0.22455 1.5718 0.22455 3.2784 0.04491 1.6617-0.13473 3.1437-0.17964 1.4371-0.58383 2.2455-3.7275 0.85329-6.467 4.3563-2.6946 3.503-4.4012 10.06l1.3473 0.31437q0.8982-3.2784 2.1108-5.8383 1.2575-2.5599 3.0988-3.997 1.8862-1.4371 4.6257-1.2575 0.26946 1.8413 0.40419 4.2215 0.13473 2.3353 0.13473 4.8503 0 2.515-0.08982 4.8503-0.08982 2.2904-0.26946 4.0868-0.17964 1.7515-0.40419 2.6048-3.4131-0.0449-6.467-0.22455-3.009-0.13472-6.2425-0.31436-3.2335-0.13473-7.2754-0.13473-8.8023 0-13.967 0.22455-5.1646 0.22454-7.3203 0.44909z"/>
|
||||
<path d="m-18.514 113.61q-0.40419-1.0778-0.58383-2.5598-0.17964-1.482-0.22455-3.0539t0.08982-2.9191q0.17964-1.3922 0.4491-2.2006 0.53892-0.17964 1.2126-0.71856 0.71856-0.58382 1.2126-2.0209 0.53892-1.4371 0.53892-4.2215 0-2.6946-0.4491-4.1766-0.40419-1.482-1.0778-2.1108t-1.4371-0.71856q-0.40419-0.94311-0.58383-2.3802-0.17964-1.482-0.17964-3.0539 0.04491-1.5718 0.26946-3.009 0.22455-1.482 0.67365-2.4251 1.0778 0.08982 2.7844 0.22455 1.7066 0.08982 3.6377 0.22455 1.976 0.08982 3.8173 0.17964 1.8413 0.04491 3.2335 0.04491 2.0209 0 4.1766-0.08982 2.2006-0.13473 4.0419-0.31437 1.8862-0.17964 3.009-0.26946 0.40419 0.94311 0.53892 2.5149 0.17964 1.5718 0.13473 3.3233-0.04491 1.7066-0.22455 3.2784-0.17964 1.5718-0.53892 2.5149-1.0778 0-1.6168 0.85329-0.49401 0.80838-0.67365 2.1557-0.13473 1.3473-0.13473 2.8742v4.6706h1.3024q-0.08982-2.1108 0-4.0868 0.13473-1.976 0.31437-2.8293 2.0659-0.35928 4.6706-0.4491 2.6497-0.13473 5.2994 0 2.6946 0.08982 4.8053 0.4491 0.26946 1.1677 0.40419 3.3233 0.17964 2.1108 0.17964 4.7155 0.04491 2.5599 0 5.0748-0.04491 2.47-0.22455 4.4012-0.13473 1.9311-0.35928 2.7844-4.491-0.0898-9.3861-0.40418-4.8503-0.26946-9.9251-0.26946-2.5149 0-5.4341 0.0449-2.8742 0.0898-5.6586 0.17964-2.7844 0.13473-4.9401 0.22454-2.1557 0.13473-3.1437 0.22455z"/>
|
||||
<path d="m48.357 114.28q-6.3772 0-9.5209-1.5269-3.0988-1.5718-3.1886-5.5239-0.58383 1.6617-0.40419 3.0988 0.22455 1.4371 0.71856 2.8293-4.8053 0.13473-7.9939 0.26945-3.1886 0.17964-5.7934 0.13473-0.67365-3.5928-0.76347-7.0957-0.04491-3.5479 0.53892-7.4999 2.47 0 4.9401 0.49401 2.47 0.49401 4.6706 1.3922 2.2006 0.85329 3.7724 2.1108l0.8982-0.9431q-1.5269-1.2126-3.3233-2.0209-1.7515-0.85329-3.5479-1.4371-2.2904-0.80838-4.1766-2.0658-1.8413-1.2575-2.9191-3.1437-1.0778-1.8862-1.0778-4.491 0-3.2335 1.482-5.8383 1.482-2.6497 4.8503-4.1766 3.4131-1.5718 9.1616-1.5718 5.7035 0 8.7125 1.6168 3.009 1.5718 3.0988 5.8383 0.62874-1.7066 0.40419-3.1886-0.22455-1.482-0.71856-3.1437 3.2335-0.04491 5.3892-0.08982 2.1557-0.08982 3.8173-0.17964 1.6617-0.08982 3.3682-0.08982 0.67365 3.2335 0.71856 7.0059 0.08982 3.7275-0.49401 7.6347-2.5599-0.04491-4.5808-0.4491-1.976-0.4491-3.6826-1.3024-1.7066-0.85329-3.503-2.1108l-0.80838 1.0329q1.7066 1.2575 3.503 2.1557 1.7964 0.85329 3.3682 1.5718 2.3353 1.0329 3.9521 2.2904 1.6168 1.2575 2.47 3.0539 0.8982 1.7964 0.8982 4.491 0 3.7275-1.3024 5.9281-1.2575 2.2006-3.3682 3.2784-2.1108 1.0329-4.6257 1.3473-2.47 0.31437-4.9401 0.31437z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 4.2 KiB |
|
@ -68,7 +68,7 @@ lsp.zls.name=Zig Language Server
|
|||
lsp.zls.description=The <a href="https://github.com/Zigtools/ZLS">Zig Language Server</a>, via ZigBrains
|
||||
settings.list.title=ZLS Instances
|
||||
settings.list.empty=Select a ZLS version to view or edit its details here
|
||||
settings.model.detected.separator=Detected ZLS version
|
||||
settings.model.detected.separator=Detected ZLS versions
|
||||
settings.model.none.text=<No ZLS>
|
||||
settings.model.loading.text=Loading\u2026
|
||||
settings.model.from-disk.text=Add ZLS from disk\u2026
|
||||
|
|
19
src/art/zls/zls.svg
Normal file
19
src/art/zls/zls.svg
Normal file
|
@ -0,0 +1,19 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="475.77" height="139.86" version="1.1" viewBox="0 0 125.88 37.006" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
|
||||
<metadata>
|
||||
<rdf:RDF>
|
||||
<cc:Work rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
|
||||
<dc:title/>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g transform="translate(-4.3455 -50.664)">
|
||||
<g transform="translate(67.635 -26.611)" fill="#f7a41d" stroke-width="1.1227" aria-label="ZLS">
|
||||
<path d="m-62.391 113.61q-0.35928-0.9431-0.58383-2.4251-0.22455-1.5269-0.26946-3.1437 0-1.6617 0.08982-3.1437 0.13473-1.5269 0.4491-2.47 1.3024-0.494 2.8742-1.1227t3.2335-1.976q1.6617-1.3922 3.1886-3.997 1.5718-2.6497 2.7395-7.0957l-1.3473-0.26946q-0.80838 3.0539-1.976 5.3892-1.1677 2.3353-3.1886 3.7724-1.976 1.4371-5.2544 1.8862-0.4491-2.8742-0.67365-6.0628-0.17964-3.2335-0.17964-6.2425 0.04491-3.009 0.22455-5.2994 0.22455-2.3353 0.49401-3.4131 3.8173 0.13473 7.2754 0.26946 3.4581 0.08982 6.9161 0.17964 3.503 0.04491 7.2754 0.04491 2.6048 0 5.5688-0.04491 3.009-0.04491 5.8383-0.08982 2.8742-0.08982 5.0748-0.13473 2.2006-0.08982 3.2335-0.17964 0.4491 1.0778 0.67365 2.6497 0.22455 1.5718 0.22455 3.2784 0.04491 1.6617-0.13473 3.1437-0.17964 1.4371-0.58383 2.2455-3.7275 0.85329-6.467 4.3563-2.6946 3.503-4.4012 10.06l1.3473 0.31437q0.8982-3.2784 2.1108-5.8383 1.2575-2.5599 3.0988-3.997 1.8862-1.4371 4.6257-1.2575 0.26946 1.8413 0.40419 4.2215 0.13473 2.3353 0.13473 4.8503 0 2.515-0.08982 4.8503-0.08982 2.2904-0.26946 4.0868-0.17964 1.7515-0.40419 2.6048-3.4131-0.0449-6.467-0.22455-3.009-0.13472-6.2425-0.31436-3.2335-0.13473-7.2754-0.13473-8.8023 0-13.967 0.22455-5.1646 0.22454-7.3203 0.44909z"/>
|
||||
<path d="m-18.514 113.61q-0.40419-1.0778-0.58383-2.5598-0.17964-1.482-0.22455-3.0539t0.08982-2.9191q0.17964-1.3922 0.4491-2.2006 0.53892-0.17964 1.2126-0.71856 0.71856-0.58382 1.2126-2.0209 0.53892-1.4371 0.53892-4.2215 0-2.6946-0.4491-4.1766-0.40419-1.482-1.0778-2.1108t-1.4371-0.71856q-0.40419-0.94311-0.58383-2.3802-0.17964-1.482-0.17964-3.0539 0.04491-1.5718 0.26946-3.009 0.22455-1.482 0.67365-2.4251 1.0778 0.08982 2.7844 0.22455 1.7066 0.08982 3.6377 0.22455 1.976 0.08982 3.8173 0.17964 1.8413 0.04491 3.2335 0.04491 2.0209 0 4.1766-0.08982 2.2006-0.13473 4.0419-0.31437 1.8862-0.17964 3.009-0.26946 0.40419 0.94311 0.53892 2.5149 0.17964 1.5718 0.13473 3.3233-0.04491 1.7066-0.22455 3.2784-0.17964 1.5718-0.53892 2.5149-1.0778 0-1.6168 0.85329-0.49401 0.80838-0.67365 2.1557-0.13473 1.3473-0.13473 2.8742v4.6706h1.3024q-0.08982-2.1108 0-4.0868 0.13473-1.976 0.31437-2.8293 2.0659-0.35928 4.6706-0.4491 2.6497-0.13473 5.2994 0 2.6946 0.08982 4.8053 0.4491 0.26946 1.1677 0.40419 3.3233 0.17964 2.1108 0.17964 4.7155 0.04491 2.5599 0 5.0748-0.04491 2.47-0.22455 4.4012-0.13473 1.9311-0.35928 2.7844-4.491-0.0898-9.3861-0.40418-4.8503-0.26946-9.9251-0.26946-2.5149 0-5.4341 0.0449-2.8742 0.0898-5.6586 0.17964-2.7844 0.13473-4.9401 0.22454-2.1557 0.13473-3.1437 0.22455z"/>
|
||||
<path d="m48.357 114.28q-6.3772 0-9.5209-1.5269-3.0988-1.5718-3.1886-5.5239-0.58383 1.6617-0.40419 3.0988 0.22455 1.4371 0.71856 2.8293-4.8053 0.13473-7.9939 0.26945-3.1886 0.17964-5.7934 0.13473-0.67365-3.5928-0.76347-7.0957-0.04491-3.5479 0.53892-7.4999 2.47 0 4.9401 0.49401 2.47 0.49401 4.6706 1.3922 2.2006 0.85329 3.7724 2.1108l0.8982-0.9431q-1.5269-1.2126-3.3233-2.0209-1.7515-0.85329-3.5479-1.4371-2.2904-0.80838-4.1766-2.0658-1.8413-1.2575-2.9191-3.1437-1.0778-1.8862-1.0778-4.491 0-3.2335 1.482-5.8383 1.482-2.6497 4.8503-4.1766 3.4131-1.5718 9.1616-1.5718 5.7035 0 8.7125 1.6168 3.009 1.5718 3.0988 5.8383 0.62874-1.7066 0.40419-3.1886-0.22455-1.482-0.71856-3.1437 3.2335-0.04491 5.3892-0.08982 2.1557-0.08982 3.8173-0.17964 1.6617-0.08982 3.3682-0.08982 0.67365 3.2335 0.71856 7.0059 0.08982 3.7275-0.49401 7.6347-2.5599-0.04491-4.5808-0.4491-1.976-0.4491-3.6826-1.3024-1.7066-0.85329-3.503-2.1108l-0.80838 1.0329q1.7066 1.2575 3.503 2.1557 1.7964 0.85329 3.3682 1.5718 2.3353 1.0329 3.9521 2.2904 1.6168 1.2575 2.47 3.0539 0.8982 1.7964 0.8982 4.491 0 3.7275-1.3024 5.9281-1.2575 2.2006-3.3682 3.2784-2.1108 1.0329-4.6257 1.3473-2.47 0.31437-4.9401 0.31437z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 4.2 KiB |
Loading…
Add table
Reference in a new issue