Skip to content

Commit

Permalink
Add LSP support [paid versions of IDEA only] (#68)
Browse files Browse the repository at this point in the history
  • Loading branch information
JojOatXGME committed Apr 29, 2024
1 parent 736f766 commit cfe2ef1
Show file tree
Hide file tree
Showing 10 changed files with 473 additions and 6 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
### Added

- Plugin logo for easier recognition
- Experimental Language Server support using IDEA's LSP API (#68)
[(Only works for paid versions of IDEA 😞)](https://blog.jetbrains.com/platform/2023/07/lsp-for-plugin-developers/#supported-ides)

### Changed

Expand All @@ -17,6 +19,8 @@

### Removed

- Support for IDEA 2023.1

### Fixed

- Variables behind `inherit` keyword not correctly resolved during highlighting
Expand Down
11 changes: 9 additions & 2 deletions build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import org.jetbrains.changelog.Changelog
import org.jetbrains.changelog.markdownToHTML
import org.jetbrains.intellij.tasks.RunPluginVerifierTask
import org.jetbrains.intellij.tasks.RunPluginVerifierTask.FailureLevel
import java.util.EnumSet

plugins {
id("java")
Expand Down Expand Up @@ -167,7 +168,13 @@ tasks {
}

runPluginVerifier {
failureLevel = RunPluginVerifierTask.FailureLevel.ALL
failureLevel = EnumSet.complementOf(
EnumSet.of(
FailureLevel.DEPRECATED_API_USAGES,
FailureLevel.SCHEDULED_FOR_REMOVAL_API_USAGES,
FailureLevel.EXPERIMENTAL_API_USAGES,
)
)
// Version 1.364 seems to be broken and always complains about supposedly missing 'plugin.xml':
// https://youtrack.jetbrains.com/issue/MP-6388
verifierVersion = "1.307"
Expand Down
6 changes: 3 additions & 3 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@
pluginGroup = org.nixos.idea
pluginName = NixIDEA
pluginVersion = 0.4.0.13
pluginSinceBuild = 231
pluginSinceBuild = 232
pluginUntilBuild = 241.*

platformType = IC
platformVersion = 2023.1.6
platformType = IU
platformVersion = 2023.2.6

# Gradle Configuration
# -> https://docs.gradle.org/current/userguide/build_environment.html#sec:gradle_configuration_properties
Expand Down
34 changes: 34 additions & 0 deletions src/main/java/org/nixos/idea/lsp/NixLspServerDescriptor.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package org.nixos.idea.lsp;

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.jetbrains.annotations.NotNull;
import org.nixos.idea.file.NixFileType;

import java.util.List;

@SuppressWarnings("UnstableApiUsage")
final class NixLspServerDescriptor extends ProjectWideLspServerDescriptor {

private final String myCommand;

NixLspServerDescriptor(@NotNull Project project, NixLspSettings settings) {
super(project, "Nix");
myCommand = settings.getCommand();
}

@Override
public @NotNull GeneralCommandLine createCommandLine() throws ExecutionException {
List<String> argv = ParametersListUtil.parse(myCommand, false, true);
return new GeneralCommandLine(argv);
}

@Override
public boolean isSupportedFile(@NotNull VirtualFile virtualFile) {
return virtualFile.getFileType() == NixFileType.INSTANCE;
}
}
26 changes: 26 additions & 0 deletions src/main/java/org/nixos/idea/lsp/NixLspServerSupportProvider.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package org.nixos.idea.lsp;

import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.platform.lsp.api.LspServerSupportProvider;
import org.jetbrains.annotations.NotNull;
import org.nixos.idea.file.NixFileType;

@SuppressWarnings("UnstableApiUsage")
public final class NixLspServerSupportProvider implements LspServerSupportProvider {
@Override
public void fileOpened(@NotNull Project project, @NotNull VirtualFile virtualFile, @NotNull LspServerStarter lspServerStarter) {
if (virtualFile.getFileType() == NixFileType.INSTANCE) {
NixLspSettings settings = NixLspSettings.getInstance();
if (settings.isEnabled()) {
lspServerStarter.ensureServerStarted(new NixLspServerDescriptor(project, settings));
}
}
}

// TODO: Uncomment with IDEA 2024.1
//@Override
//public @NotNull LspServerWidgetItem createLspServerWidgetItem(@NotNull LspServer lspServer, @Nullable VirtualFile currentFile) {
// return new LspServerWidgetItem(lspServer, currentFile, NixIcons.FILE, NixLspSettingsConfigurable.class);
//}
}
78 changes: 78 additions & 0 deletions src/main/java/org/nixos/idea/lsp/NixLspSettings.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package org.nixos.idea.lsp;

import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.components.PersistentStateComponent;
import com.intellij.openapi.components.RoamingType;
import com.intellij.openapi.components.State;
import com.intellij.openapi.components.Storage;
import org.jetbrains.annotations.NotNull;

import java.util.ArrayDeque;
import java.util.Collection;
import java.util.Collections;
import java.util.Deque;

@State(name = "NixLspSettings", storages = @Storage(value = "nix-idea-tools.xml", roamingType = RoamingType.DISABLED))
public final class NixLspSettings implements PersistentStateComponent<NixLspSettings.State> {

// TODO: Use RoamingType.LOCAL with 2024.1

// Documentation:
// https://plugins.jetbrains.com/docs/intellij/persisting-state-of-components.html

private static final int MAX_HISTORY_SIZE = 5;

private @NotNull State myState = new State();

public static @NotNull NixLspSettings getInstance() {
return ApplicationManager.getApplication().getService(NixLspSettings.class);
}

public boolean isEnabled() {
return myState.enabled;
}

public void setEnabled(boolean enabled) {
myState.enabled = enabled;
}

public @NotNull String getCommand() {
return myState.command;
}

public void setCommand(@NotNull String command) {
myState.command = command;
addToHistory(command);
}

public @NotNull Collection<String> getCommandHistory() {
return Collections.unmodifiableCollection(myState.history);
}

private void addToHistory(@NotNull String command) {
Deque<String> history = myState.history;
history.remove(command);
history.addFirst(command);
while (history.size() > MAX_HISTORY_SIZE) {
history.removeLast();
}
}

@SuppressWarnings("ClassEscapesDefinedScope")
@Override
public void loadState(@NotNull State state) {
myState = state;
}

@SuppressWarnings("ClassEscapesDefinedScope")
@Override
public @NotNull State getState() {
return myState;
}

static final class State {
public boolean enabled = false;
public @NotNull String command = "";
public Deque<String> history = new ArrayDeque<>();
}
}
99 changes: 99 additions & 0 deletions src/main/java/org/nixos/idea/lsp/NixLspSettingsConfigurable.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package org.nixos.idea.lsp;

import com.intellij.openapi.options.Configurable;
import com.intellij.openapi.options.ConfigurationException;
import com.intellij.openapi.options.SearchableConfigurable;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.project.ProjectManager;
import com.intellij.openapi.util.NlsContexts;
import com.intellij.platform.lsp.api.LspServerManager;
import com.intellij.ui.RawCommandLineEditor;
import com.intellij.ui.TitledSeparator;
import com.intellij.ui.components.JBCheckBox;
import com.intellij.util.ui.FormBuilder;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.nixos.idea.lsp.ui.CommandSuggestionsPopup;

import javax.swing.JComponent;
import javax.swing.JPanel;

public class NixLspSettingsConfigurable implements SearchableConfigurable, Configurable.Beta {

private @Nullable JBCheckBox myEnabled;
private @Nullable RawCommandLineEditor myCommand;

@Override
public @NotNull @NonNls String getId() {
return "org.nixos.idea.lsp.NixLspSettings";
}

@Override
public @NlsContexts.ConfigurableName String getDisplayName() {
return "Language Server (LSP)";
}

@Override
public @Nullable JComponent createComponent() {
myEnabled = new JBCheckBox("Enable language server");
myEnabled.addChangeListener(e -> updateUiState());

myCommand = new RawCommandLineEditor();
myCommand.getEditorField().getEmptyText().setText("Command to start Language Server");
myCommand.getEditorField().getAccessibleContext().setAccessibleName("Command to start Language Server");
myCommand.getEditorField().setMargin(myEnabled.getMargin());
new CommandSuggestionsPopup(myCommand, NixLspSettings.getInstance().getCommandHistory()).install();

return FormBuilder.createFormBuilder()
.addComponent(myEnabled)
.addComponent(new TitledSeparator("Language Server Configuration"))
.addLabeledComponent("Command: ", myCommand)
.addComponentFillVertically(new JPanel(), 0)
.getPanel();
}

@Override
public void reset() {
assert myEnabled != null;
assert myCommand != null;

NixLspSettings settings = NixLspSettings.getInstance();
myEnabled.setSelected(settings.isEnabled());
myCommand.setText(settings.getCommand());

updateUiState();
}

@SuppressWarnings("UnstableApiUsage")
@Override
public void apply() throws ConfigurationException {
assert myEnabled != null;
assert myCommand != null;

NixLspSettings settings = NixLspSettings.getInstance();
settings.setEnabled(myEnabled.isSelected());
settings.setCommand(myCommand.getText());

for (Project project : ProjectManager.getInstance().getOpenProjects()) {
LspServerManager.getInstance(project).stopAndRestartIfNeeded(NixLspServerSupportProvider.class);
}
}

@Override
public boolean isModified() {
assert myEnabled != null;
assert myCommand != null;

NixLspSettings settings = NixLspSettings.getInstance();
return Configurable.isCheckboxModified(myEnabled, settings.isEnabled()) ||
Configurable.isFieldModified(myCommand.getTextField(), settings.getCommand());
}

private void updateUiState() {
assert myEnabled != null;
assert myCommand != null;

myCommand.setEnabled(myEnabled.isSelected());
}
}
Loading

0 comments on commit cfe2ef1

Please sign in to comment.