Compare commits
176 commits
Author | SHA1 | Date | |
---|---|---|---|
0032dbf0f3 | |||
e7f0bb793a | |||
532292068f | |||
dd2104065c | |||
8389c66814 | |||
803b25d7fb | |||
b3ce6d62a7 | |||
f28cf77347 | |||
c524bbf899 | |||
a9c8a3a0d9 | |||
570eeac755 | |||
5efc28ae18 | |||
91747319aa | |||
8a7ca04168 | |||
5e4f2d5dfb | |||
4722613b4c | |||
53f4d1d330 | |||
270ac9e113 | |||
a4db054b59 | |||
bd47cb201f | |||
520167414a | |||
2d0ad8be44 | |||
9913ceb67e | |||
d5359e5816 | |||
8336d2bcc5 | |||
281ce0ed4e | |||
dcede7eb43 | |||
137977f691 | |||
ab20a57e9e | |||
1725b189a4 | |||
68b60e2c77 | |||
f7ea73ae45 | |||
3ceb61f2dd | |||
c7e33ea8de | |||
12e5ffdea1 | |||
54cd514249 | |||
a8f97172d6 | |||
b485c1e48c | |||
ee5a2463b9 | |||
9676b70821 | |||
9023026478 | |||
8bb4e8bef1 | |||
9541bb9752 | |||
2c500d40a5 | |||
e737058cb5 | |||
f53b0e3283 | |||
3de2f92bf8 | |||
dec12a8041 | |||
ac90dab503 | |||
9830b7ebc9 | |||
efa2f127a9 | |||
78c4751c53 | |||
1f79f484e5 | |||
6c14ad7113 | |||
ab5948a51b | |||
a950c932f5 | |||
46069b9a22 | |||
0a5a765eaf | |||
da38433eb3 | |||
7c1aa36d82 | |||
ceea101170 | |||
6dacf97583 | |||
b497f04e40 | |||
13266d112c | |||
74f1ceb880 | |||
af9ebee500 | |||
cc062b533e | |||
612841724c | |||
0cbce88d10 | |||
1ce31d786d | |||
8880f22840 | |||
94fe15409a | |||
868c37c567 | |||
e33c96d658 | |||
f02f50c1a7 | |||
9faa421ad9 | |||
009db1cb0f | |||
5c682f6bfa | |||
cc7d1393d6 | |||
7ee7e2f3d1 | |||
f138a2a4ba | |||
7c14ca2944 | |||
7c0fb4412d | |||
cb4ecb9ff6 | |||
7da3af7cd0 | |||
09ec2d79cc | |||
2f8bc57fe1 | |||
b2d4355488 | |||
1382aed48c | |||
404a554654 | |||
206cc1e437 | |||
c6f3a8202b | |||
4f9c324af1 | |||
a9b55ec830 | |||
08a04aff2c | |||
fbeb7985e4 | |||
c6af369b1c | |||
2ab3570d08 | |||
d4a1b69172 | |||
91ee38e922 | |||
05ff125c1d | |||
03a86defb9 | |||
a1c952e019 | |||
5399fbe2a9 | |||
f1bbe84c71 | |||
d80230cba5 | |||
e2406b406c | |||
e177001020 | |||
f57d78f8c3 | |||
683aa7fad1 | |||
c31a78d547 | |||
16fb4b57b7 | |||
88df5b1426 | |||
6b8b82e710 | |||
f26be52940 | |||
0982b3488d | |||
0b22a4538b | |||
ec66112700 | |||
8e9968b6f5 | |||
2730b88c18 | |||
efdde4aad2 | |||
2694b9e1c1 | |||
aaa8885081 | |||
353151c4c5 | |||
009c5fa5ed | |||
2e6db5ccd3 | |||
26c4b26ae8 | |||
63d45b87b5 | |||
9ed15e49cf | |||
df15261196 | |||
dcce9a8b28 | |||
993930fa99 | |||
ecf01829cf | |||
87d30dfaa2 | |||
4990cb42f6 | |||
3a5daab063 | |||
0edcce333b | |||
9135cc6dd6 | |||
14c909d50e | |||
aace0d316f | |||
9d05a4c3d7 | |||
a3143dbc0c | |||
adb8797051 | |||
ff75518aa8 | |||
694acfc1d3 | |||
6879cbf0e5 | |||
77e72ea98d | |||
8a988d5bfe | |||
c652e1f05c | |||
91e8857ea9 | |||
990563d70a | |||
0636ba09f6 | |||
f9317ebe29 | |||
66d76b99a4 | |||
a32068ce23 | |||
93d8deeec8 | |||
6a67748355 | |||
27160d6778 | |||
0ed6adff8d | |||
17dca6d419 | |||
d3de75f950 | |||
f067ca647c | |||
c9a3388c57 | |||
7ab0657fd4 | |||
e6be5d7995 | |||
e45872a98b | |||
24da7b413a | |||
fbdeb371f7 | |||
7120d095d1 | |||
9994416282 | |||
5fa4ff72d1 | |||
5eca261831 | |||
768b8b29ff | |||
aeeb2ab946 | |||
28527cddb4 | |||
f704b6c936 |
37 changed files with 300 additions and 69 deletions
|
@ -152,6 +152,7 @@ Changelog structure reference:
|
|||
|
||||
- Project
|
||||
- Occasional "AWT events are not allowed inside write action" error coming from LSP
|
||||
- IllegalStateException coming from the standard library handler
|
||||
|
||||
## [22.0.0]
|
||||
|
||||
|
@ -194,6 +195,8 @@ This (and newer) versions of the plugin will automatically upgrade tasks from 21
|
|||
|
||||
### Added
|
||||
|
||||
- Zig
|
||||
- Changing the zig standard library path in the project settings now properly updates the dependency
|
||||
- ZLS
|
||||
- All of the config options are now exposed in the GUI
|
||||
|
||||
|
|
|
@ -6,8 +6,8 @@ import org.jetbrains.kotlin.gradle.dsl.KotlinJvmProjectExtension
|
|||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask
|
||||
|
||||
plugins {
|
||||
kotlin("jvm") version "1.9.22" apply false
|
||||
kotlin("plugin.serialization") version "1.9.22" apply false
|
||||
kotlin("jvm") version "2.1.10" apply false
|
||||
kotlin("plugin.serialization") version "2.1.10" apply false
|
||||
id("org.jetbrains.intellij.platform") version "2.5.0"
|
||||
id("org.jetbrains.changelog") version "2.2.1"
|
||||
id("org.jetbrains.grammarkit") version "2022.3.2.2" apply false
|
||||
|
@ -90,6 +90,7 @@ allprojects {
|
|||
|
||||
intellijPlatform {
|
||||
defaultRepositories()
|
||||
snapshots()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -58,7 +58,8 @@ class ZigClionDebuggerDriverConfigurationProvider: ZigDebuggerDriverConfiguratio
|
|||
return when(toolchain.debuggerKind) {
|
||||
CPPDebugger.Kind.BUNDLED_GDB,
|
||||
CPPDebugger.Kind.CUSTOM_GDB -> CLionGDBDriverConfiguration(project, toolchain, isEmulateTerminal = emulateTerminal)
|
||||
CPPDebugger.Kind.BUNDLED_LLDB -> CLionLLDBDriverConfiguration(project, toolchain, isEmulateTerminal = emulateTerminal)
|
||||
CPPDebugger.Kind.BUNDLED_LLDB,
|
||||
CPPDebugger.Kind.CUSTOM_LLDB -> CLionLLDBDriverConfiguration(project, toolchain, isEmulateTerminal = emulateTerminal)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -211,7 +211,7 @@ abstract class DAPDriver<Server : IDebugProtocolServer, Client : IDebugProtocolC
|
|||
val cli = installer.install()
|
||||
val args = HashMap<String, Any>()
|
||||
args["program"] = Util.toWinPath(cli.exePath)
|
||||
args["cmd"] = cli.workDirectory.toString()
|
||||
args["cmd"] = cli.workingDirectory.toString()
|
||||
args["name"] = "CPP Debug"
|
||||
args["type"] = "cppvsdbg"
|
||||
args["request"] = "launch"
|
||||
|
@ -954,7 +954,7 @@ abstract class DAPDriver<Server : IDebugProtocolServer, Client : IDebugProtocolC
|
|||
cli.withCharset(Charsets.UTF_8)
|
||||
val cwd = args.cwd?.ifBlank { null }?.toNioPathOrNull()
|
||||
if (cwd != null) {
|
||||
cli.withWorkDirectory(cwd.toFile())
|
||||
cli.withWorkingDirectory(cwd)
|
||||
}
|
||||
val childProcess = ZigProcessHandler(cli)
|
||||
this@DAPDriver.childProcess = childProcess
|
||||
|
|
|
@ -34,7 +34,7 @@ class ZigProfileStateBinary(environment: ExecutionEnvironment, configuration: Zi
|
|||
override suspend fun getCommandLine(toolchain: ZigToolchain, debug: Boolean): GeneralCommandLine {
|
||||
val cli = GeneralCommandLine()
|
||||
val cfg = configuration
|
||||
cfg.workingDirectory.path?.let { cli.withWorkDirectory(it.toFile()) }
|
||||
cfg.workingDirectory.path?.let { cli.withWorkingDirectory(it) }
|
||||
cli.withExePath(cfg.exePath.path?.pathString ?: throw ExecutionException(ZigDebugBundle.message("exception.missing-exe-path")))
|
||||
cli.withCharset(Charsets.UTF_8)
|
||||
cli.addParameters(cfg.args.args)
|
||||
|
|
|
@ -41,7 +41,7 @@ class ZigDebugEmitBinaryInstaller<ProfileState: ZigProfileState<*>>(
|
|||
override fun install(): GeneralCommandLine {
|
||||
val cfg = profileState.configuration
|
||||
val cli = PtyCommandLine().withConsoleMode(false).withExePath(executableFile.absolutePath)
|
||||
cfg.workingDirectory.path?.let { x -> cli.withWorkDirectory(x.toFile()) }
|
||||
cfg.workingDirectory.path?.let { x -> cli.withWorkingDirectory(x) }
|
||||
cli.addParameters(exeArgs)
|
||||
cli.withCharset(Charsets.UTF_8)
|
||||
cli.withRedirectErrorStream(true)
|
||||
|
|
|
@ -29,6 +29,7 @@ import com.intellij.openapi.options.SimpleConfigurable
|
|||
import com.intellij.util.xmlb.XmlSerializerUtil
|
||||
import com.intellij.xdebugger.settings.DebuggerSettingsCategory
|
||||
import com.intellij.xdebugger.settings.XDebuggerSettings
|
||||
import java.util.function.Supplier
|
||||
|
||||
class ZigDebuggerSettings: XDebuggerSettings<ZigDebuggerSettings>("Zig") {
|
||||
var debuggerKind = DebuggerKind.default
|
||||
|
@ -58,9 +59,10 @@ class ZigDebuggerSettings: XDebuggerSettings<ZigDebuggerSettings>("Zig") {
|
|||
GENERAL_SETTINGS_ID,
|
||||
ZigDebugBundle.message("settings.debugger.title"),
|
||||
ZigDebuggerGeneralSettingsConfigurableUi::class.java,
|
||||
) {
|
||||
instance
|
||||
}
|
||||
Supplier {
|
||||
instance
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
|
|
@ -29,12 +29,12 @@ import com.falsepattern.zigbrains.debugger.toolchain.ZigDebuggerToolchainService
|
|||
import com.falsepattern.zigbrains.shared.coroutine.withCurrentEDTModalityContext
|
||||
import com.intellij.notification.Notification
|
||||
import com.intellij.notification.NotificationType
|
||||
import com.intellij.openapi.progress.coroutineToIndicator
|
||||
import com.intellij.openapi.ui.DialogBuilder
|
||||
import com.intellij.platform.util.progress.withProgressText
|
||||
import com.intellij.ui.components.JBLabel
|
||||
import com.intellij.ui.components.JBPanel
|
||||
import com.intellij.util.download.DownloadableFileService
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import kotlinx.coroutines.withTimeoutOrNull
|
||||
|
@ -96,7 +96,7 @@ private suspend fun downloadMSVCProps(): Properties {
|
|||
val downloader = service.createDownloader(listOf(desc), "Debugger metadata downloading")
|
||||
val downloadDirectory = downloadPath().toFile()
|
||||
val prop = Properties()
|
||||
val downloadResults = runBlocking {
|
||||
val downloadResults = coroutineToIndicator {
|
||||
downloader.download(downloadDirectory)
|
||||
}
|
||||
for (result in downloadResults) {
|
||||
|
|
|
@ -30,7 +30,7 @@ import com.intellij.openapi.application.PathManager
|
|||
import com.intellij.openapi.components.Service
|
||||
import com.intellij.openapi.components.service
|
||||
import com.intellij.openapi.diagnostic.logger
|
||||
import com.intellij.openapi.progress.blockingContext
|
||||
import com.intellij.openapi.progress.coroutineToIndicator
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.ui.DialogBuilder
|
||||
import com.intellij.openapi.util.SystemInfo
|
||||
|
@ -222,7 +222,7 @@ class ZigDebuggerToolchainService {
|
|||
val downloader = service.createDownloader(descriptions, "Debugger downloading")
|
||||
val downloadDirectory = downloadPath().toFile()
|
||||
val downloadResults = reporter.sizedStep(100) {
|
||||
blockingContext {
|
||||
coroutineToIndicator {
|
||||
downloader.download(downloadDirectory)
|
||||
}
|
||||
}
|
||||
|
@ -233,7 +233,7 @@ class ZigDebuggerToolchainService {
|
|||
val propertyName = binaryToDownload.propertyName
|
||||
val archiveFile = result.first
|
||||
reporter.indeterminateStep {
|
||||
blockingContext {
|
||||
coroutineToIndicator {
|
||||
Unarchiver.unarchive(archiveFile.toPath(), baseDir, binaryToDownload.prefix)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,7 +42,7 @@ abstract class MSVCDriverConfiguration: DAPDebuggerDriverConfiguration() {
|
|||
val cli = GeneralCommandLine()
|
||||
cli.withExePath(path.pathString)
|
||||
cli.addParameters("--interpreter=vscode", "--extconfigdir=%USERPROFILE%\\.cppvsdbg\\extensions")
|
||||
cli.withWorkDirectory(path.parent.toFile())
|
||||
cli.withWorkingDirectory(path.parent)
|
||||
return cli
|
||||
}
|
||||
|
||||
|
|
|
@ -103,7 +103,7 @@ class DirenvService(val project: Project): SerializablePersistentStateComponent<
|
|||
}
|
||||
|
||||
private suspend fun run(workDir: Path, vararg args: String): DirenvOutput {
|
||||
val cli = GeneralCommandLine("direnv", *args).withWorkDirectory(workDir.toFile())
|
||||
val cli = GeneralCommandLine("direnv", *args).withWorkingDirectory(workDir)
|
||||
|
||||
val (process, exitCode) = withProgressText("Running ${cli.commandLineString}") {
|
||||
withContext(Dispatchers.IO) {
|
||||
|
|
|
@ -160,7 +160,6 @@ class WorkDirectoryConfigurable(@Transient override val serializedName: String)
|
|||
class WorkDirectoryConfigModule(private val serializedName: String) : PathConfigModule<WorkDirectoryConfigurable>() {
|
||||
private val field = textFieldWithBrowseButton(
|
||||
null,
|
||||
ZigBrainsBundle.message("dialog.title.working-directory"),
|
||||
FileChooserDescriptorFactory.createSingleFolderDescriptor().withTitle(ZigBrainsBundle.message("dialog.title.working-directory"))
|
||||
).also { Disposer.register(this, it) }
|
||||
|
||||
|
@ -199,8 +198,7 @@ class FilePathConfigurable(
|
|||
class FilePathConfigModule(private val serializedName: String, @Nls private val label: String) : PathConfigModule<FilePathConfigurable>() {
|
||||
private val field = textFieldWithBrowseButton(
|
||||
null,
|
||||
null,
|
||||
FileChooserDescriptorFactory.createSingleFileDescriptor(),
|
||||
FileChooserDescriptorFactory.createSingleFileNoJarsDescriptor(),
|
||||
)
|
||||
|
||||
override var stringValue by field::text
|
||||
|
|
|
@ -66,7 +66,7 @@ abstract class ZigProfileState<T: ZigExecConfig<T>> (
|
|||
|
||||
val cli = PtyCommandLine().withConsoleMode(false)
|
||||
cli.withExePath(zigExePath.pathString)
|
||||
workingDir.path?.let { cli.withWorkDirectory(it.toFile()) }
|
||||
workingDir.path?.let { cli.withWorkingDirectory(it) }
|
||||
cli.withCharset(Charsets.UTF_8)
|
||||
cli.addParameters(configuration.buildCommandLineArgs(debug))
|
||||
return configuration.patchCommandLine(cli)
|
||||
|
|
|
@ -68,7 +68,7 @@ class ZigModuleBuilder: ModuleBuilder() {
|
|||
internal val peer = ZigProjectGeneratorPeer(true).also { Disposer.register(parent ?: return@also) {it.dispose()} }
|
||||
|
||||
override fun getComponent(): JComponent {
|
||||
return peer.component.withBorder()
|
||||
return peer.myComponent.withBorder()
|
||||
}
|
||||
|
||||
override fun disposeUIResources() {
|
||||
|
|
|
@ -50,7 +50,7 @@ class ZigNewProjectWizard: LanguageGeneratorNewProjectWizard {
|
|||
|
||||
override fun setupUI(builder: Panel): Unit = with(builder) {
|
||||
row {
|
||||
cell(peer.component).align(AlignX.FILL)
|
||||
cell(peer.myComponent).align(AlignX.FILL)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
package com.falsepattern.zigbrains.project.newproject
|
||||
|
||||
import com.intellij.ide.util.projectWizard.SettingsStep
|
||||
import com.intellij.openapi.ui.TextFieldWithBrowseButton
|
||||
import com.intellij.openapi.ui.ValidationInfo
|
||||
import com.intellij.platform.ProjectGeneratorPeer
|
||||
import com.intellij.ui.dsl.builder.panel
|
||||
|
@ -32,12 +33,13 @@ class ZigProjectGeneratorPeer(var handleGit: Boolean): ProjectGeneratorPeer<ZigP
|
|||
val newProjectPanel by lazy {
|
||||
ZigNewProjectPanel(handleGit)
|
||||
}
|
||||
private val myComponent: JComponent by lazy {
|
||||
val myComponent: JComponent by lazy {
|
||||
panel {
|
||||
newProjectPanel.attach(this)
|
||||
}
|
||||
}
|
||||
override fun getComponent(): JComponent {
|
||||
|
||||
override fun getComponent(myLocationField: TextFieldWithBrowseButton, checkValid: Runnable): JComponent {
|
||||
return myComponent
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* This file is part of ZigBrains.
|
||||
*
|
||||
* Copyright (C) 2023-2025 FalsePattern
|
||||
* All Rights Reserved
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* ZigBrains is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, only version 3 of the License.
|
||||
*
|
||||
* ZigBrains is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with ZigBrains. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.falsepattern.zigbrains.project.stdlib
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.roots.AdditionalLibraryRootsProvider
|
||||
import com.intellij.openapi.roots.SyntheticLibrary
|
||||
|
||||
class ZigLibraryRootProvider: AdditionalLibraryRootsProvider() {
|
||||
override fun getAdditionalProjectLibraries(project: Project): Collection<SyntheticLibrary> {
|
||||
return setOf(ZigSyntheticLibrary(project))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,190 @@
|
|||
/*
|
||||
* This file is part of ZigBrains.
|
||||
*
|
||||
* Copyright (C) 2023-2025 FalsePattern
|
||||
* All Rights Reserved
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* ZigBrains is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, only version 3 of the License.
|
||||
*
|
||||
* ZigBrains is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with ZigBrains. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.falsepattern.zigbrains.project.stdlib
|
||||
|
||||
import com.falsepattern.zigbrains.Icons
|
||||
import com.falsepattern.zigbrains.project.toolchain.ZigToolchainService
|
||||
import com.falsepattern.zigbrains.project.toolchain.base.ZigToolchain
|
||||
import com.falsepattern.zigbrains.project.toolchain.local.LocalZigToolchain
|
||||
import com.intellij.navigation.ItemPresentation
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.project.guessProjectDir
|
||||
import com.intellij.openapi.roots.SyntheticLibrary
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.openapi.vfs.refreshAndFindVirtualDirectory
|
||||
import com.intellij.platform.backend.workspace.WorkspaceModel
|
||||
import com.intellij.platform.backend.workspace.toVirtualFileUrl
|
||||
import com.intellij.platform.workspace.jps.entities.*
|
||||
import com.intellij.project.isDirectoryBased
|
||||
import com.intellij.project.stateStore
|
||||
import com.intellij.workspaceModel.ide.legacyBridge.LegacyBridgeJpsEntitySourceFactory
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import java.util.*
|
||||
import javax.swing.Icon
|
||||
|
||||
class ZigSyntheticLibrary(val project: Project) : SyntheticLibrary(), ItemPresentation {
|
||||
private var toolchain: ZigToolchain? = ZigToolchainService.getInstance(project).toolchain
|
||||
private val roots by lazy {
|
||||
runBlocking {getRoot(toolchain, project)}?.let { setOf(it) } ?: emptySet()
|
||||
}
|
||||
|
||||
private val name by lazy {
|
||||
getName(toolchain, project)
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other !is ZigSyntheticLibrary)
|
||||
return false
|
||||
|
||||
return toolchain == other.toolchain
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return Objects.hash(roots)
|
||||
}
|
||||
|
||||
override fun getPresentableText(): String {
|
||||
return name
|
||||
}
|
||||
|
||||
override fun getIcon(unused: Boolean): Icon {
|
||||
return Icons.Zig
|
||||
}
|
||||
|
||||
override fun getSourceRoots(): Collection<VirtualFile> {
|
||||
return roots
|
||||
}
|
||||
|
||||
override fun isShowInExternalLibrariesNode(): Boolean {
|
||||
return !roots.isEmpty()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val ZIG_LIBRARY_ID = "Zig SDK"
|
||||
private const val ZIG_MODULE_ID = "ZigBrains"
|
||||
private val libraryTableId = LibraryTableId.ProjectLibraryTableId
|
||||
private val libraryId = LibraryId(ZIG_LIBRARY_ID, libraryTableId)
|
||||
private val moduleId = ModuleId(ZIG_MODULE_ID)
|
||||
suspend fun reload(project: Project, toolchain: ZigToolchain?) {
|
||||
val root = getRoot(toolchain, project)
|
||||
if (root != null) {
|
||||
add(project, root)
|
||||
} else {
|
||||
remove(project)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun remove(project: Project) {
|
||||
val workspaceModel = WorkspaceModel.getInstance(project)
|
||||
workspaceModel.update("Update Zig std") { builder ->
|
||||
builder.resolve(moduleId)?.let { moduleEntity ->
|
||||
builder.removeEntity(moduleEntity)
|
||||
}
|
||||
builder.resolve(libraryId)?.let { libraryEntity ->
|
||||
builder.removeEntity(libraryEntity)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun add(project: Project, root: VirtualFile) {
|
||||
val workspaceModel = WorkspaceModel.getInstance(project)
|
||||
val libRoot = LibraryRoot(root.toVirtualFileUrl(workspaceModel.getVirtualFileUrlManager()), LibraryRootTypeId.SOURCES)
|
||||
|
||||
var baseModuleDirFile: VirtualFile? = null
|
||||
if (project.isDirectoryBased) {
|
||||
baseModuleDirFile = project.stateStore.directoryStorePath?.refreshAndFindVirtualDirectory()
|
||||
}
|
||||
if (baseModuleDirFile == null) {
|
||||
baseModuleDirFile = project.guessProjectDir()
|
||||
}
|
||||
val baseModuleDir = baseModuleDirFile?.toVirtualFileUrl(workspaceModel.getVirtualFileUrlManager()) ?: return
|
||||
workspaceModel.update("Update Zig std") { builder ->
|
||||
builder.resolve(moduleId)?.let { moduleEntity ->
|
||||
builder.removeEntity(moduleEntity)
|
||||
}
|
||||
val moduleEntitySource = LegacyBridgeJpsEntitySourceFactory.getInstance(project)
|
||||
.createEntitySourceForModule(baseModuleDir, null)
|
||||
|
||||
val moduleEntity = builder.addEntity(ModuleEntity(ZIG_MODULE_ID, emptyList(), moduleEntitySource))
|
||||
|
||||
builder.resolve(libraryId)?.let { libraryEntity ->
|
||||
builder.removeEntity(libraryEntity)
|
||||
}
|
||||
val libraryEntitySource = LegacyBridgeJpsEntitySourceFactory
|
||||
.getInstance(project)
|
||||
.createEntitySourceForProjectLibrary(null)
|
||||
val libraryEntity = LibraryEntity(
|
||||
ZIG_LIBRARY_ID,
|
||||
libraryTableId, emptyList(),
|
||||
libraryEntitySource
|
||||
) {
|
||||
roots.add(libRoot)
|
||||
}
|
||||
builder.addEntity(libraryEntity)
|
||||
builder.modifyModuleEntity(moduleEntity) {
|
||||
val dep = LibraryDependency(libraryId, false, DependencyScope.COMPILE)
|
||||
dependencies.clear()
|
||||
dependencies.add(dep)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getName(
|
||||
toolchain: ZigToolchain?,
|
||||
project: Project
|
||||
): String {
|
||||
val tc = toolchain ?: return "Zig"
|
||||
toolchain.name?.let { return it }
|
||||
runBlocking { tc.zig.getEnv(project) }
|
||||
.mapCatching { it.version }
|
||||
.getOrNull()
|
||||
?.let { return "Zig $it" }
|
||||
return "Zig"
|
||||
}
|
||||
|
||||
suspend fun getRoot(
|
||||
toolchain: ZigToolchain?,
|
||||
project: Project
|
||||
): VirtualFile? {
|
||||
//TODO universal
|
||||
if (toolchain !is LocalZigToolchain) {
|
||||
return null
|
||||
}
|
||||
if (toolchain.std != null) run {
|
||||
val ePath = toolchain.std
|
||||
if (ePath.isAbsolute) {
|
||||
val roots = ePath.refreshAndFindVirtualDirectory() ?: return@run
|
||||
return roots
|
||||
}
|
||||
val stdPath = toolchain.location.resolve(ePath)
|
||||
if (stdPath.isAbsolute) {
|
||||
val roots = stdPath.refreshAndFindVirtualDirectory() ?: return@run
|
||||
return roots
|
||||
}
|
||||
}
|
||||
val stdPath = toolchain.zig.getEnv(project).mapCatching { it.stdPath(toolchain, project) }.getOrNull() ?: return null
|
||||
val roots = stdPath.refreshAndFindVirtualDirectory() ?: return null
|
||||
return roots
|
||||
}
|
|
@ -22,11 +22,16 @@
|
|||
|
||||
package com.falsepattern.zigbrains.project.toolchain
|
||||
|
||||
import com.falsepattern.zigbrains.project.stdlib.ZigSyntheticLibrary
|
||||
import com.falsepattern.zigbrains.project.toolchain.base.ZigToolchain
|
||||
import com.falsepattern.zigbrains.shared.asUUID
|
||||
import com.falsepattern.zigbrains.shared.zigCoroutineScope
|
||||
import com.intellij.openapi.application.EDT
|
||||
import com.intellij.openapi.components.*
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.util.xmlb.annotations.Attribute
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import java.util.*
|
||||
|
||||
@Service(Service.Level.PROJECT)
|
||||
|
@ -50,6 +55,9 @@ class ZigToolchainService(private val project: Project): SerializablePersistentS
|
|||
updateState {
|
||||
it.copy(toolchain = value?.toString() ?: "")
|
||||
}
|
||||
zigCoroutineScope.launch(Dispatchers.EDT) {
|
||||
ZigSyntheticLibrary.reload(project, toolchain)
|
||||
}
|
||||
}
|
||||
|
||||
val toolchain: ZigToolchain?
|
||||
|
|
|
@ -56,7 +56,7 @@ class LocalToolchainSelector(component: Component): LocalSelector<LocalZigToolch
|
|||
} else {
|
||||
val existingToolchain = zigToolchainList
|
||||
.mapNotNull { it.second as? LocalZigToolchain }
|
||||
.firstOrNull { it.location == tc!!.location }
|
||||
.firstOrNull { it.location == tc.location }
|
||||
if (existingToolchain != null) {
|
||||
result = VerifyResult(
|
||||
null,
|
||||
|
|
|
@ -27,7 +27,7 @@ import com.falsepattern.zigbrains.shared.downloader.VersionInfo
|
|||
import com.falsepattern.zigbrains.shared.downloader.VersionInfo.Tarball
|
||||
import com.falsepattern.zigbrains.shared.downloader.getTarballIfCompatible
|
||||
import com.falsepattern.zigbrains.shared.downloader.tempPluginDir
|
||||
import com.intellij.openapi.progress.blockingContext
|
||||
import com.intellij.openapi.progress.coroutineToIndicator
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import com.intellij.util.asSafely
|
||||
import com.intellij.util.download.DownloadableFileService
|
||||
|
@ -54,7 +54,7 @@ data class ZigVersionInfo(
|
|||
val tempFile = FileUtil.createTempFile(tempPluginDir, "index", ".json", false, false)
|
||||
val desc = service.createFileDescription("https://ziglang.org/download/index.json", tempFile.name)
|
||||
val downloader = service.createDownloader(listOf(desc), ZigBrainsBundle.message("settings.toolchain.downloader.service.index"))
|
||||
val downloadResults = blockingContext {
|
||||
val downloadResults = coroutineToIndicator {
|
||||
downloader.download(tempPluginDir)
|
||||
}
|
||||
if (downloadResults.isEmpty())
|
||||
|
|
|
@ -47,8 +47,7 @@ import kotlin.io.path.pathString
|
|||
class LocalZigToolchainPanel() : ImmutableNamedElementPanelBase<LocalZigToolchain>() {
|
||||
private val pathToToolchain = textFieldWithBrowseButton(
|
||||
null,
|
||||
ZigBrainsBundle.message("dialog.title.zig-toolchain"),
|
||||
FileChooserDescriptorFactory.createSingleFolderDescriptor()
|
||||
FileChooserDescriptorFactory.createSingleFolderDescriptor().withTitle(ZigBrainsBundle.message("dialog.title.zig-toolchain"))
|
||||
).also {
|
||||
it.textField.document.addDocumentListener(object : DocumentAdapter() {
|
||||
override fun textChanged(e: DocumentEvent) {
|
||||
|
@ -70,8 +69,7 @@ class LocalZigToolchainPanel() : ImmutableNamedElementPanelBase<LocalZigToolchai
|
|||
}
|
||||
private val pathToStd = textFieldWithBrowseButton(
|
||||
null,
|
||||
ZigBrainsBundle.message("dialog.title.zig-std"),
|
||||
FileChooserDescriptorFactory.createSingleFolderDescriptor()
|
||||
FileChooserDescriptorFactory.createSingleFolderDescriptor().withTitle(ZigBrainsBundle.message("dialog.title.zig-std"))
|
||||
).also { Disposer.register(this, it) }
|
||||
private var debounce: Job? = null
|
||||
|
||||
|
|
|
@ -31,7 +31,6 @@ import com.intellij.icons.AllIcons
|
|||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.ui.SimpleTextAttributes
|
||||
import com.intellij.ui.icons.EMPTY_ICON
|
||||
import com.intellij.util.ui.EmptyIcon
|
||||
import javax.swing.JList
|
||||
|
||||
class TCComboBox(model: ZBModel<ZigToolchain>): ZBComboBox<ZigToolchain>(model, ::TCCellRenderer)
|
||||
|
@ -68,7 +67,7 @@ class TCCellRenderer(getModel: () -> ZBModel<ZigToolchain>): ZBCellRenderer<ZigT
|
|||
append(ZigBrainsBundle.message("settings.toolchain.model.from-disk.text"))
|
||||
}
|
||||
is ListElem.Pending -> {
|
||||
icon = EMPTY_ICON
|
||||
icon = AllIcons.Empty
|
||||
append(ZigBrainsBundle.message("settings.toolchain.model.loading.text"), SimpleTextAttributes.GRAYED_ATTRIBUTES)
|
||||
}
|
||||
is ListElem.None, null -> {
|
||||
|
@ -78,5 +77,4 @@ class TCCellRenderer(getModel: () -> ZBModel<ZigToolchain>): ZBCellRenderer<ZigT
|
|||
}
|
||||
}
|
||||
|
||||
}
|
||||
private val EMPTY_ICON = EmptyIcon.create(16, 16)
|
||||
}
|
|
@ -123,7 +123,7 @@ fun createCommandLineSafe(
|
|||
return Result.failure(IllegalArgumentException("file is a directory: ${exe.pathString}"))
|
||||
val cli = GeneralCommandLine()
|
||||
.withExePath(exe.toString())
|
||||
.withWorkDirectory(workingDirectory?.toFile())
|
||||
.withWorkingDirectory(workingDirectory)
|
||||
.withParameters(*parameters)
|
||||
.withCharset(Charsets.UTF_8)
|
||||
return Result.success(cli)
|
||||
|
|
|
@ -33,7 +33,7 @@ import kotlinx.coroutines.*
|
|||
import java.awt.Component
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
||||
inline fun <T> runModalOrBlocking(taskOwnerFactory: () -> ModalTaskOwner, titleFactory: () -> String, cancellationFactory: () -> TaskCancellation = TaskCancellation::cancellable, noinline action: suspend CoroutineScope.() -> T): T {
|
||||
inline fun <T> runModalOrBlocking(taskOwnerFactory: () -> ModalTaskOwner, titleFactory: () -> String, cancellationFactory: () -> TaskCancellation = {TaskCancellation.cancellable()}, noinline action: suspend CoroutineScope.() -> T): T {
|
||||
return if (application.isDispatchThread) {
|
||||
runWithModalProgressBlocking(taskOwnerFactory(), titleFactory(), cancellationFactory(), action)
|
||||
} else {
|
||||
|
|
|
@ -96,7 +96,7 @@ abstract class Downloader<T, V: VersionInfo>(val component: Component) {
|
|||
value?.let { append(it.version.rawVersion) }
|
||||
}
|
||||
}
|
||||
val outputPath = textFieldWithBrowseButton(null, selector.descriptor.title, selector.descriptor)
|
||||
val outputPath = textFieldWithBrowseButton(null, selector.descriptor)
|
||||
Disposer.register(dialog, outputPath)
|
||||
outputPath.textField.columns = 50
|
||||
|
||||
|
|
|
@ -66,7 +66,7 @@ abstract class LocalSelector<T>(val component: Component) {
|
|||
private suspend fun doBrowseFromDisk(preSelected: Path?): T? {
|
||||
val dialog = DialogBuilder()
|
||||
val name = JBTextField().also { it.columns = 25 }
|
||||
val path = textFieldWithBrowseButton(null, descriptor.title, descriptor)
|
||||
val path = textFieldWithBrowseButton(null, descriptor)
|
||||
Disposer.register(dialog, path)
|
||||
lateinit var errorMessageBox: JBLabel
|
||||
suspend fun verifyAndUpdate(path: Path?) {
|
||||
|
|
|
@ -28,7 +28,7 @@ import com.falsepattern.zigbrains.shared.downloader.VersionInfo.Tarball
|
|||
import com.intellij.openapi.application.PathManager
|
||||
import com.intellij.openapi.progress.EmptyProgressIndicator
|
||||
import com.intellij.openapi.progress.ProgressManager
|
||||
import com.intellij.openapi.progress.blockingContext
|
||||
import com.intellij.openapi.progress.coroutineToIndicator
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import com.intellij.openapi.util.io.toNioPathOrNull
|
||||
import com.intellij.platform.util.progress.ProgressReporter
|
||||
|
@ -83,7 +83,7 @@ suspend fun downloadTarball(dist: Tarball, into: Path, reporter: ProgressReporte
|
|||
val desc = service.createFileDescription(dist.tarball, tempFile.name)
|
||||
val downloader = service.createDownloader(listOf(desc), ZigBrainsBundle.message("settings.toolchain.downloader.service.tarball"))
|
||||
val downloadResults = reporter.sizedStep(100) {
|
||||
blockingContext {
|
||||
coroutineToIndicator {
|
||||
downloader.download(into.toFile())
|
||||
}
|
||||
}
|
||||
|
@ -99,7 +99,7 @@ suspend fun flattenDownloadDir(dir: Path, reporter: ProgressReporter) {
|
|||
if (contents.size == 1 && contents[0].isDirectory()) {
|
||||
val src = contents[0]
|
||||
reporter.indeterminateStep {
|
||||
blockingContext {
|
||||
coroutineToIndicator {
|
||||
val indicator = ProgressManager.getInstance().progressIndicator ?: EmptyProgressIndicator()
|
||||
indicator.isIndeterminate = true
|
||||
indicator.text = ZigBrainsBundle.message("settings.toolchain.downloader.progress.flatten")
|
||||
|
@ -121,7 +121,7 @@ suspend fun unpackTarball(tarball: Path, into: Path, reporter: ProgressReporter)
|
|||
withContext(Dispatchers.IO) {
|
||||
try {
|
||||
reporter.indeterminateStep {
|
||||
blockingContext {
|
||||
coroutineToIndicator {
|
||||
Unarchiver.unarchive(tarball, into)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -87,7 +87,7 @@ abstract class UUIDMapSelector<T>(val driver: UUIDComboBoxDriver<T>): Disposable
|
|||
val actual = item is ListElem.One.Actual<*>
|
||||
editButton?.isEnabled = actual
|
||||
editButton?.repaint()
|
||||
onSelection(if (actual) (item as ListElem.One.Actual<*>).uuid else null)
|
||||
onSelection(if (actual) item.uuid else null)
|
||||
}
|
||||
|
||||
private fun itemStateChanged(event: ItemEvent) {
|
||||
|
|
|
@ -76,13 +76,9 @@ private fun <T : PsiElement> ZigMultilineAssistant<T>.preprocessEnter(
|
|||
val indentPost = parts[1].measureSpaces()
|
||||
val newLine = StringBuilder(1 + indentPre + prefix.length + indentPost)
|
||||
.append('\n')
|
||||
for (i in 0..<indentPre) {
|
||||
newLine.append(' ')
|
||||
}
|
||||
newLine.append(prefix)
|
||||
for (i in 0..<indentPost) {
|
||||
newLine.append(' ')
|
||||
}
|
||||
.repeat(' '.code, indentPre)
|
||||
.append(prefix)
|
||||
.repeat(' '.code, indentPost)
|
||||
document.insertString(offset, newLine)
|
||||
PsiDocumentManager.getInstance(project).commitDocument(document)
|
||||
editor.caretModel.moveToOffset(offset + newLine.length)
|
||||
|
|
|
@ -165,6 +165,10 @@
|
|||
implementation="com.falsepattern.zigbrains.project.console.ZigSourceFileFilter"
|
||||
/>
|
||||
|
||||
<additionalLibraryRootsProvider
|
||||
implementation="com.falsepattern.zigbrains.project.stdlib.ZigLibraryRootProvider"
|
||||
/>
|
||||
|
||||
<!--suppress PluginXmlValidity -->
|
||||
<toolWindow
|
||||
factoryClass="com.falsepattern.zigbrains.project.steps.ui.BuildToolWindowFactory"
|
||||
|
|
|
@ -3,13 +3,13 @@ pluginRepositoryUrl=https://github.com/FalsePattern/ZigBrains
|
|||
|
||||
pluginVersion=25.2.0
|
||||
|
||||
pluginSinceBuild=241
|
||||
pluginUntilBuild=241.*
|
||||
pluginSinceBuild=251
|
||||
pluginUntilBuild=
|
||||
|
||||
ideaCommunityVersion=2024.1.7
|
||||
clionVersion=2024.1.6
|
||||
ideaCommunityVersion=2025.1
|
||||
clionVersion=2025.1
|
||||
useInstaller=true
|
||||
javaVersion=17
|
||||
javaVersion=21
|
||||
# ideaCommunity / clion
|
||||
runIdeTarget=clion
|
||||
|
||||
|
@ -17,7 +17,7 @@ lsp4jVersion=0.21.1
|
|||
lsp4ijVersion=0.12.0
|
||||
lsp4ijNightly=false
|
||||
|
||||
serializationVersion=1.6.3
|
||||
serializationVersion=1.7.3
|
||||
|
||||
kotlin.stdlib.default.dependency=false
|
||||
kotlin.code.style=official
|
||||
|
|
|
@ -46,7 +46,7 @@ class ZLSStreamConnectionProvider private constructor(private val project: Proje
|
|||
companion object {
|
||||
suspend fun create(project: Project): ZLSStreamConnectionProvider {
|
||||
val projectDir = project.guessProjectDir()?.toNioPathOrNull()
|
||||
val commandLine = getCommand(project)?.let { GeneralCommandLine(it) }?.withWorkDirectory(projectDir?.toFile())
|
||||
val commandLine = getCommand(project)?.let { GeneralCommandLine(it) }?.withWorkingDirectory(projectDir)
|
||||
return ZLSStreamConnectionProvider(project, commandLine)
|
||||
}
|
||||
|
||||
|
|
|
@ -43,8 +43,8 @@ import javax.swing.text.PlainDocument
|
|||
class ZLSSettingsPanel() : ImmutableElementPanel<ZLSSettings> {
|
||||
private val zlsConfigPath = textFieldWithBrowseButton(
|
||||
null,
|
||||
ZLSBundle.message("settings.zls-config-path.browse.title"),
|
||||
FileChooserDescriptorFactory.createSingleFileNoJarsDescriptor()
|
||||
.withTitle(ZLSBundle.message("settings.zls-config-path.browse.title"))
|
||||
).also { Disposer.register(this, it) }
|
||||
private val inlayHints = JBCheckBox()
|
||||
private val inlayHintsMaxFileSize = ExtendableTextField(5).also { (it.document as PlainDocument).documentFilter = object: DocumentFilter() {
|
||||
|
|
|
@ -49,8 +49,7 @@ import kotlin.io.path.pathString
|
|||
class ZLSPanel() : ImmutableNamedElementPanelBase<ZLSVersion>() {
|
||||
private val pathToZLS = textFieldWithBrowseButton(
|
||||
null,
|
||||
ZLSBundle.message("dialog.title.zls"),
|
||||
FileChooserDescriptorFactory.createSingleFileNoJarsDescriptor()
|
||||
FileChooserDescriptorFactory.createSingleFileNoJarsDescriptor().withTitle(ZLSBundle.message("dialog.title.zls"))
|
||||
).also {
|
||||
it.textField.document.addDocumentListener(object : DocumentAdapter() {
|
||||
override fun textChanged(e: DocumentEvent) {
|
||||
|
|
|
@ -28,7 +28,7 @@ import com.falsepattern.zigbrains.shared.downloader.VersionInfo
|
|||
import com.falsepattern.zigbrains.shared.downloader.VersionInfo.Tarball
|
||||
import com.falsepattern.zigbrains.shared.downloader.getTarballIfCompatible
|
||||
import com.falsepattern.zigbrains.shared.downloader.tempPluginDir
|
||||
import com.intellij.openapi.progress.blockingContext
|
||||
import com.intellij.openapi.progress.coroutineToIndicator
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import com.intellij.util.asSafely
|
||||
|
@ -52,7 +52,7 @@ data class ZLSVersionInfo(
|
|||
return withContext(Dispatchers.IO) {
|
||||
val single = toolchain != null
|
||||
val url = if (single) {
|
||||
getToolchainURL(toolchain!!, project) ?: return@withContext emptyList()
|
||||
getToolchainURL(toolchain, project) ?: return@withContext emptyList()
|
||||
} else {
|
||||
multiURL
|
||||
}
|
||||
|
@ -60,7 +60,7 @@ data class ZLSVersionInfo(
|
|||
val tempFile = FileUtil.createTempFile(tempPluginDir, "zls_version_info", ".json", false, false)
|
||||
val desc = service.createFileDescription(url, tempFile.name)
|
||||
val downloader = service.createDownloader(listOf(desc), ZLSBundle.message("settings.downloader.service.index"))
|
||||
val downloadResults = blockingContext {
|
||||
val downloadResults = coroutineToIndicator {
|
||||
downloader.download(tempPluginDir)
|
||||
}
|
||||
if (downloadResults.isEmpty())
|
||||
|
|
|
@ -30,7 +30,6 @@ import com.intellij.icons.AllIcons
|
|||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.ui.SimpleTextAttributes
|
||||
import com.intellij.ui.icons.EMPTY_ICON
|
||||
import com.intellij.util.ui.EmptyIcon
|
||||
import javax.swing.JList
|
||||
import kotlin.io.path.pathString
|
||||
|
||||
|
@ -71,7 +70,7 @@ class ZLSCellRenderer(getModel: () -> ZBModel<ZLSVersion>): ZBCellRenderer<ZLSVe
|
|||
append(ZLSBundle.message("settings.model.from-disk.text"))
|
||||
}
|
||||
is ListElem.Pending -> {
|
||||
icon = EMPTY_ICON
|
||||
icon = AllIcons.Empty
|
||||
append(ZLSBundle.message("settings.model.loading.text"), SimpleTextAttributes.GRAYED_ATTRIBUTES)
|
||||
}
|
||||
is ListElem.None, null -> {
|
||||
|
@ -81,5 +80,4 @@ class ZLSCellRenderer(getModel: () -> ZBModel<ZLSVersion>): ZBCellRenderer<ZLSVe
|
|||
}
|
||||
}
|
||||
|
||||
}
|
||||
private val EMPTY_ICON = EmptyIcon.create(16, 16)
|
||||
}
|
Loading…
Add table
Reference in a new issue