From 15d16b335c95ea0a4e866b77e6f57ae324b53e8f Mon Sep 17 00:00:00 2001 From: Ayk Borstelmann Date: Tue, 30 Dec 2025 13:13:25 +0100 Subject: [PATCH 1/5] Fix #86: Allow configuration of workspace Migrate Lsp Settings to Project Settings, since the workspace configuration is project specific and the editor text field requires a project --- gradle/productsReleases.txt | 1 + .../idea/lsp/NixLspServerDescriptor.java | 21 ++++++++-- .../idea/lsp/NixLspServerSupportProvider.java | 2 +- .../java/org/nixos/idea/lsp/NixLspSettings.kt | 12 ++++-- .../idea/lsp/NixLspSettingsConfigurable.kt | 39 ++++++++++++++++--- .../resources/META-INF/nix-idea-ultimate.xml | 8 ++-- 6 files changed, 66 insertions(+), 17 deletions(-) diff --git a/gradle/productsReleases.txt b/gradle/productsReleases.txt index 7b324665..27efc333 100644 --- a/gradle/productsReleases.txt +++ b/gradle/productsReleases.txt @@ -1,3 +1,4 @@ +IU-2025.3.1 IU-2025.1.2 IU-2024.3.6 IU-2024.2.6 diff --git a/src/main/java/org/nixos/idea/lsp/NixLspServerDescriptor.java b/src/main/java/org/nixos/idea/lsp/NixLspServerDescriptor.java index b73bd6f7..ab8ae8f9 100644 --- a/src/main/java/org/nixos/idea/lsp/NixLspServerDescriptor.java +++ b/src/main/java/org/nixos/idea/lsp/NixLspServerDescriptor.java @@ -1,32 +1,47 @@ package org.nixos.idea.lsp; +import com.google.gson.JsonElement; +import com.google.gson.JsonParser; import com.intellij.execution.ExecutionException; import com.intellij.execution.configurations.GeneralCommandLine; import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.platform.lsp.api.ProjectWideLspServerDescriptor; import com.intellij.util.execution.ParametersListUtil; +import org.eclipse.lsp4j.ConfigurationItem; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.nixos.idea.file.NixFileType; import java.util.List; +import java.util.Optional; @SuppressWarnings("UnstableApiUsage") final class NixLspServerDescriptor extends ProjectWideLspServerDescriptor { - private final String myCommand; + private final NixLspSettings settings; NixLspServerDescriptor(@NotNull Project project, NixLspSettings settings) { super(project, "Nix"); - myCommand = settings.getCommand(); + this.settings = settings; + getClientCapabilities().getWorkspace().setConfiguration(true); } @Override public @NotNull GeneralCommandLine createCommandLine() throws ExecutionException { - List argv = ParametersListUtil.parse(myCommand, false, true); + List argv = ParametersListUtil.parse(settings.getCommand(), false, true); return new GeneralCommandLine(argv); } + @Override + public @Nullable Object getWorkspaceConfiguration(@NotNull ConfigurationItem item) { + return Optional.ofNullable(settings.getConfiguration()) + .map(JsonParser::parseString) + .map(JsonElement::getAsJsonObject) + .map(jsonObject -> jsonObject.get(item.getSection())) + .orElse(null); + } + @Override public boolean isSupportedFile(@NotNull VirtualFile virtualFile) { return virtualFile.getFileType() == NixFileType.INSTANCE; diff --git a/src/main/java/org/nixos/idea/lsp/NixLspServerSupportProvider.java b/src/main/java/org/nixos/idea/lsp/NixLspServerSupportProvider.java index 20d0d197..e6702416 100644 --- a/src/main/java/org/nixos/idea/lsp/NixLspServerSupportProvider.java +++ b/src/main/java/org/nixos/idea/lsp/NixLspServerSupportProvider.java @@ -15,7 +15,7 @@ public final class NixLspServerSupportProvider implements LspServerSupportProvid @Override public void fileOpened(@NotNull Project project, @NotNull VirtualFile virtualFile, @NotNull LspServerStarter lspServerStarter) { if (virtualFile.getFileType() == NixFileType.INSTANCE) { - NixLspSettings settings = NixLspSettings.getInstance(); + NixLspSettings settings = NixLspSettings.getInstance(project); if (settings.isEnabled()) { lspServerStarter.ensureServerStarted(new NixLspServerDescriptor(project, settings)); } diff --git a/src/main/java/org/nixos/idea/lsp/NixLspSettings.kt b/src/main/java/org/nixos/idea/lsp/NixLspSettings.kt index 5b8b3c20..0e340e5a 100644 --- a/src/main/java/org/nixos/idea/lsp/NixLspSettings.kt +++ b/src/main/java/org/nixos/idea/lsp/NixLspSettings.kt @@ -1,17 +1,19 @@ package org.nixos.idea.lsp -import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.components.BaseState import com.intellij.openapi.components.RoamingType +import com.intellij.openapi.components.Service import com.intellij.openapi.components.SimplePersistentStateComponent import com.intellij.openapi.components.State import com.intellij.openapi.components.Storage +import com.intellij.openapi.project.Project import org.nixos.idea.settings.NixStoragePaths import org.nixos.idea.settings.SimplePersistentStateComponentHelper.delegate import java.util.ArrayDeque import java.util.Collections import java.util.Deque +@Service(Service.Level.PROJECT) @State(name = "NixLspSettings", storages = [Storage(value = NixStoragePaths.TOOLS, roamingType = RoamingType.LOCAL)]) class NixLspSettings : SimplePersistentStateComponent(State()) { @@ -19,17 +21,21 @@ class NixLspSettings : SimplePersistentStateComponent(Stat var enabled by property(false) var command by string() var history: Deque by property(ArrayDeque(), { it.isEmpty() }) + var configuration by string(DEFAULT_CONFIGURATION) } var isEnabled: Boolean by delegate(State::enabled) var command: String by delegate(State::command, State::history) val commandHistory: Collection get() = Collections.unmodifiableCollection(state.history) + var configuration: String? by delegate(State::configuration) companion object { + private const val DEFAULT_CONFIGURATION: String = "{}" + @JvmStatic - fun getInstance(): NixLspSettings { - return ApplicationManager.getApplication().getService(NixLspSettings::class.java) + fun getInstance(project: Project): NixLspSettings { + return project.getService(NixLspSettings::class.java) } } } diff --git a/src/main/java/org/nixos/idea/lsp/NixLspSettingsConfigurable.kt b/src/main/java/org/nixos/idea/lsp/NixLspSettingsConfigurable.kt index 12730612..8b79a389 100644 --- a/src/main/java/org/nixos/idea/lsp/NixLspSettingsConfigurable.kt +++ b/src/main/java/org/nixos/idea/lsp/NixLspSettingsConfigurable.kt @@ -1,16 +1,21 @@ package org.nixos.idea.lsp +import com.intellij.json.JsonFileType +import com.intellij.openapi.editor.ex.EditorEx import com.intellij.openapi.options.BoundSearchableConfigurable import com.intellij.openapi.options.Configurable -import com.intellij.openapi.project.ProjectManager +import com.intellij.openapi.project.Project import com.intellij.platform.lsp.api.LspServerManager +import com.intellij.ui.EditorTextField import com.intellij.ui.RawCommandLineEditor import com.intellij.ui.components.JBCheckBox +import com.intellij.ui.dsl.builder.Align import com.intellij.ui.dsl.builder.AlignX import com.intellij.ui.dsl.builder.Cell import com.intellij.ui.dsl.builder.bindSelected import com.intellij.ui.dsl.builder.panel import com.intellij.ui.dsl.builder.selected +import com.intellij.ui.dsl.builder.toMutableProperty import org.nixos.idea.settings.ui.CommandSuggestionsPopup import org.nixos.idea.settings.ui.UiDslExtensions.bindText import org.nixos.idea.settings.ui.UiDslExtensions.placeholderText @@ -18,13 +23,14 @@ import org.nixos.idea.settings.ui.UiDslExtensions.suggestionsPopup import org.nixos.idea.settings.ui.UiDslExtensions.validateOnReset import org.nixos.idea.settings.ui.UiDslExtensions.validateWhenTextChanged import org.nixos.idea.settings.ui.UiDslExtensions.warnOnInput +import java.awt.Dimension -class NixLspSettingsConfigurable : +class NixLspSettingsConfigurable(val project: Project) : BoundSearchableConfigurable("Language Server (LSP)", "org.nixos.idea.lsp.NixLspSettingsConfigurable"), Configurable.Beta { override fun createPanel() = panel { - val settings = NixLspSettings.getInstance() + val settings = NixLspSettings.getInstance(project) lateinit var enabledCheckBox: Cell row { enabledCheckBox = checkBox("Enable language server") @@ -43,6 +49,17 @@ class NixLspSettingsConfigurable : it.text.isNullOrBlank() } } + + row { + label("Workspace configuration:").resizableColumn() + } + row { + cell(createJsonEditorTextField()).align(Align.FILL) + .bind( + EditorTextField::getText, EditorTextField::setText, + settings::configuration.toMutableProperty() + ) + }.resizableRow() }.enabledIf(enabledCheckBox.selected) } @@ -50,9 +67,21 @@ class NixLspSettingsConfigurable : override fun apply() { super.apply() reset() // Update UI components to use normalized property values - for (project in ProjectManager.getInstance().openProjects) { - LspServerManager.getInstance(project).stopAndRestartIfNeeded(NixLspServerSupportProvider::class.java) + LspServerManager.getInstance(project).stopAndRestartIfNeeded(NixLspServerSupportProvider::class.java) + } + + private fun NixLspSettingsConfigurable.createJsonEditorTextField(): EditorTextField { + val editorTextField = object : EditorTextField(null, project, JsonFileType.INSTANCE, false, false) { + override fun createEditor(): EditorEx { + val editor = super.createEditor() + editor.setHorizontalScrollbarVisible(true) + editor.setVerticalScrollbarVisible(true) + editor.settings.isUseSoftWraps = true + return editor + } } + editorTextField.preferredSize = Dimension(0, 150) + return editorTextField } } diff --git a/src/main/resources/META-INF/nix-idea-ultimate.xml b/src/main/resources/META-INF/nix-idea-ultimate.xml index 1e1a0f1a..23ab84e1 100644 --- a/src/main/resources/META-INF/nix-idea-ultimate.xml +++ b/src/main/resources/META-INF/nix-idea-ultimate.xml @@ -1,10 +1,8 @@ + com.intellij.modules.json - - - - + \ No newline at end of file From 4b3522b6b207fc3dd59dbbf2bbdcb98a0d8df0a0 Mon Sep 17 00:00:00 2001 From: Ayk Borstelmann Date: Thu, 1 Jan 2026 14:37:01 +0100 Subject: [PATCH 2/5] fixup! Add trailing new line --- src/main/resources/META-INF/nix-idea-ultimate.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/META-INF/nix-idea-ultimate.xml b/src/main/resources/META-INF/nix-idea-ultimate.xml index 23ab84e1..2fb7d946 100644 --- a/src/main/resources/META-INF/nix-idea-ultimate.xml +++ b/src/main/resources/META-INF/nix-idea-ultimate.xml @@ -12,4 +12,4 @@ implementation="org.nixos.idea.lsp.NixLspServerSupportProvider"/> - \ No newline at end of file + From 17520c695c416f2f193401a1aed08e7c55edfa13 Mon Sep 17 00:00:00 2001 From: Ayk Borstelmann Date: Thu, 1 Jan 2026 14:56:40 +0100 Subject: [PATCH 3/5] fixup! Use label in dsl syntax --- .../org/nixos/idea/lsp/NixLspSettingsConfigurable.kt | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/nixos/idea/lsp/NixLspSettingsConfigurable.kt b/src/main/java/org/nixos/idea/lsp/NixLspSettingsConfigurable.kt index 8b79a389..b2f5e345 100644 --- a/src/main/java/org/nixos/idea/lsp/NixLspSettingsConfigurable.kt +++ b/src/main/java/org/nixos/idea/lsp/NixLspSettingsConfigurable.kt @@ -12,6 +12,7 @@ import com.intellij.ui.components.JBCheckBox import com.intellij.ui.dsl.builder.Align import com.intellij.ui.dsl.builder.AlignX import com.intellij.ui.dsl.builder.Cell +import com.intellij.ui.dsl.builder.LabelPosition import com.intellij.ui.dsl.builder.bindSelected import com.intellij.ui.dsl.builder.panel import com.intellij.ui.dsl.builder.selected @@ -49,12 +50,10 @@ class NixLspSettingsConfigurable(val project: Project) : it.text.isNullOrBlank() } } - - row { - label("Workspace configuration:").resizableColumn() - } row { - cell(createJsonEditorTextField()).align(Align.FILL) + cell(createJsonEditorTextField()) + .align(Align.FILL) + .label("Workspace configuration:", LabelPosition.TOP) .bind( EditorTextField::getText, EditorTextField::setText, settings::configuration.toMutableProperty() @@ -80,7 +79,6 @@ class NixLspSettingsConfigurable(val project: Project) : return editor } } - editorTextField.preferredSize = Dimension(0, 150) return editorTextField } } From b7c49f7e988595e5e80a10418e8d5faf954852ef Mon Sep 17 00:00:00 2001 From: Ayk Borstelmann Date: Thu, 1 Jan 2026 15:41:39 +0100 Subject: [PATCH 4/5] fixup! Reduce constructor complexity --- .../nixos/idea/lsp/NixLspServerDescriptor.java | 18 +++++++++++------- .../idea/lsp/NixLspServerSupportProvider.java | 2 +- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/nixos/idea/lsp/NixLspServerDescriptor.java b/src/main/java/org/nixos/idea/lsp/NixLspServerDescriptor.java index ab8ae8f9..07a9d206 100644 --- a/src/main/java/org/nixos/idea/lsp/NixLspServerDescriptor.java +++ b/src/main/java/org/nixos/idea/lsp/NixLspServerDescriptor.java @@ -8,6 +8,7 @@ import com.intellij.openapi.vfs.VirtualFile; import com.intellij.platform.lsp.api.ProjectWideLspServerDescriptor; import com.intellij.util.execution.ParametersListUtil; +import org.eclipse.lsp4j.ClientCapabilities; import org.eclipse.lsp4j.ConfigurationItem; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -19,23 +20,26 @@ @SuppressWarnings("UnstableApiUsage") final class NixLspServerDescriptor extends ProjectWideLspServerDescriptor { - private final NixLspSettings settings; - - NixLspServerDescriptor(@NotNull Project project, NixLspSettings settings) { + NixLspServerDescriptor(@NotNull Project project) { super(project, "Nix"); - this.settings = settings; - getClientCapabilities().getWorkspace().setConfiguration(true); + } + + @Override + public @NotNull ClientCapabilities getClientCapabilities() { + ClientCapabilities clientCapabilities = super.getClientCapabilities(); + clientCapabilities.getWorkspace().setConfiguration(true); + return clientCapabilities; } @Override public @NotNull GeneralCommandLine createCommandLine() throws ExecutionException { - List argv = ParametersListUtil.parse(settings.getCommand(), false, true); + List argv = ParametersListUtil.parse(NixLspSettings.getInstance(getProject()).getCommand(), false, true); return new GeneralCommandLine(argv); } @Override public @Nullable Object getWorkspaceConfiguration(@NotNull ConfigurationItem item) { - return Optional.ofNullable(settings.getConfiguration()) + return Optional.ofNullable(NixLspSettings.getInstance(getProject()).getConfiguration()) .map(JsonParser::parseString) .map(JsonElement::getAsJsonObject) .map(jsonObject -> jsonObject.get(item.getSection())) diff --git a/src/main/java/org/nixos/idea/lsp/NixLspServerSupportProvider.java b/src/main/java/org/nixos/idea/lsp/NixLspServerSupportProvider.java index e6702416..ff0cd055 100644 --- a/src/main/java/org/nixos/idea/lsp/NixLspServerSupportProvider.java +++ b/src/main/java/org/nixos/idea/lsp/NixLspServerSupportProvider.java @@ -17,7 +17,7 @@ public void fileOpened(@NotNull Project project, @NotNull VirtualFile virtualFil if (virtualFile.getFileType() == NixFileType.INSTANCE) { NixLspSettings settings = NixLspSettings.getInstance(project); if (settings.isEnabled()) { - lspServerStarter.ensureServerStarted(new NixLspServerDescriptor(project, settings)); + lspServerStarter.ensureServerStarted(new NixLspServerDescriptor(project)); } } } From 1562d0fb0de909fd9700aac83c1d5f5c82c46388 Mon Sep 17 00:00:00 2001 From: Ayk Borstelmann Date: Thu, 1 Jan 2026 15:53:02 +0100 Subject: [PATCH 5/5] fixup! remove scrollbar visible definitions --- src/main/java/org/nixos/idea/lsp/NixLspSettingsConfigurable.kt | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/java/org/nixos/idea/lsp/NixLspSettingsConfigurable.kt b/src/main/java/org/nixos/idea/lsp/NixLspSettingsConfigurable.kt index b2f5e345..c2edbf39 100644 --- a/src/main/java/org/nixos/idea/lsp/NixLspSettingsConfigurable.kt +++ b/src/main/java/org/nixos/idea/lsp/NixLspSettingsConfigurable.kt @@ -73,8 +73,6 @@ class NixLspSettingsConfigurable(val project: Project) : val editorTextField = object : EditorTextField(null, project, JsonFileType.INSTANCE, false, false) { override fun createEditor(): EditorEx { val editor = super.createEditor() - editor.setHorizontalScrollbarVisible(true) - editor.setVerticalScrollbarVisible(true) editor.settings.isUseSoftWraps = true return editor }