diff --git a/src/main/java/com/redhat/devtools/lsp4ij/launching/ui/NewLanguageServerDialog.java b/src/main/java/com/redhat/devtools/lsp4ij/launching/ui/NewLanguageServerDialog.java index a853e1823..efc43917d 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/launching/ui/NewLanguageServerDialog.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/launching/ui/NewLanguageServerDialog.java @@ -7,12 +7,12 @@ * * Contributors: * Red Hat, Inc. - initial API and implementation + * Mitja Leino - Extend ValidatableDialog for validations ******************************************************************************/ package com.redhat.devtools.lsp4ij.launching.ui; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.ComboBox; -import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.ui.ValidationInfo; import com.intellij.ui.DocumentAdapter; import com.intellij.ui.SimpleListCellRenderer; @@ -25,6 +25,7 @@ import com.redhat.devtools.lsp4ij.launching.templates.LanguageServerTemplateManager; import com.redhat.devtools.lsp4ij.server.definition.launching.UserDefinedLanguageServerDefinition; import com.redhat.devtools.lsp4ij.settings.ui.LanguageServerPanel; +import com.redhat.devtools.lsp4ij.settings.ui.ValidatableDialog; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -40,7 +41,7 @@ /** * New language server dialog. */ -public class NewLanguageServerDialog extends DialogWrapper { +public class NewLanguageServerDialog extends ValidatableDialog { private final ComboBox templateCombo = new ComboBox<>(new DefaultComboBoxModel<>(getLanguageServerTemplates())); private final Project project; @@ -70,7 +71,7 @@ public NewLanguageServerDialog(@NotNull Project project) { // Template combo createTemplateCombo(builder); // Create server name, command line, mappings, configuration UI - this.languageServerPanel = new LanguageServerPanel(builder, null, LanguageServerPanel.EditionMode.NEW_USER_DEFINED); + this.languageServerPanel = new LanguageServerPanel(builder, null, LanguageServerPanel.EditionMode.NEW_USER_DEFINED, this); // Add validation addValidator(this.languageServerPanel.getServerName()); @@ -165,38 +166,9 @@ public JComponent getPreferredFocusedComponent() { @Override protected @NotNull List doValidateAll() { - List validations = new ArrayList<>(); - addValidationInfo(validateServerName(), validations); - addValidationInfo(validateCommand(), validations); - return validations; + return languageServerPanel.doValidateAll(); } - private void addValidationInfo(ValidationInfo validationInfo, List validations) { - if (validationInfo == null) { - return; - } - validations.add((validationInfo)); - } - - private ValidationInfo validateServerName() { - var serverName = this.languageServerPanel.getServerName(); - if (serverName.getText().isBlank()) { - String errorMessage = LanguageServerBundle.message("new.language.server.dialog.validation.serverName.must.be.set"); - return new ValidationInfo(errorMessage, serverName); - } - return null; - } - - private ValidationInfo validateCommand() { - var commandLine = this.languageServerPanel.getCommandLine(); - if (commandLine.getText().isBlank()) { - String errorMessage = LanguageServerBundle.message("new.language.server.dialog.validation.commandLine.must.be.set"); - return new ValidationInfo(errorMessage, commandLine); - } - return null; - } - - @Override protected void doOKAction() { super.doOKAction(); diff --git a/src/main/java/com/redhat/devtools/lsp4ij/settings/LanguageServerView.java b/src/main/java/com/redhat/devtools/lsp4ij/settings/LanguageServerView.java index 6ab45a938..024408b3f 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/settings/LanguageServerView.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/settings/LanguageServerView.java @@ -10,6 +10,7 @@ * * Contributors: * Red Hat Inc. - initial API and implementation + * Mitja Leino - Extend ValidatableDialog for validations *******************************************************************************/ package com.redhat.devtools.lsp4ij.settings; @@ -19,6 +20,7 @@ import com.intellij.openapi.fileTypes.FileNameMatcher; import com.intellij.openapi.fileTypes.FileType; import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.ValidationInfo; import com.intellij.ui.IdeBorderFactory; import com.intellij.util.ui.FormBuilder; import com.intellij.util.ui.JBUI; @@ -31,13 +33,13 @@ import com.redhat.devtools.lsp4ij.server.definition.launching.UserDefinedLanguageServerDefinition; import com.redhat.devtools.lsp4ij.settings.ui.LanguageServerPanel; import com.redhat.devtools.lsp4ij.settings.ui.ServerMappingsPanel; +import com.redhat.devtools.lsp4ij.settings.ui.ValidatableDialog; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; import javax.swing.border.TitledBorder; -import java.util.List; -import java.util.Objects; +import java.util.*; import java.util.stream.Collectors; /** @@ -50,7 +52,7 @@ *
  • Suspend and wait for a debugger?
  • * */ -public class LanguageServerView implements Disposable { +public class LanguageServerView extends ValidatableDialog implements Disposable { private final LanguageServerNameProvider languageServerNameProvider; @@ -70,6 +72,7 @@ public LanguageServerView(@NotNull LanguageServerDefinition languageServerDefini @Nullable LanguageServerNameProvider languageServerNameProvider, @NotNull Project project ) { + super(project); this.languageServerDefinition = languageServerDefinition; this.languageServerNameProvider = languageServerNameProvider; this.project = project; @@ -285,7 +288,7 @@ private JPanel createSettings(JComponent description, boolean launchingServerDef this.languageServerPanel = new LanguageServerPanel(builder, description, launchingServerDefinition ? LanguageServerPanel.EditionMode.EDIT_USER_DEFINED : - LanguageServerPanel.EditionMode.EDIT_EXTENSION); + LanguageServerPanel.EditionMode.EDIT_EXTENSION, this); this.mappingPanel = languageServerPanel.getMappingsPanel(); return builder .addComponentFillVertically(new JPanel(), 50) @@ -410,6 +413,16 @@ public void setInitializationOptionsContent(String initializationOptionsContent) initializationOptions.setCaretPosition(0); } + @Override + protected @Nullable JComponent createCenterPanel() { + return myMainPanel; + } + + @Override + public @NotNull List doValidateAll() { + return languageServerPanel.doValidateAll(); + } + @Override public void dispose() { } diff --git a/src/main/java/com/redhat/devtools/lsp4ij/settings/ui/CommandLineWidget.java b/src/main/java/com/redhat/devtools/lsp4ij/settings/ui/CommandLineWidget.java index 57c3dd281..2a96caa44 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/settings/ui/CommandLineWidget.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/settings/ui/CommandLineWidget.java @@ -10,17 +10,26 @@ * * Contributors: * Red Hat Inc. - initial API and implementation + * Mitja Leino - Implement ValidatableConsoleWidget *******************************************************************************/ package com.redhat.devtools.lsp4ij.settings.ui; +import com.intellij.openapi.ui.ValidationInfo; import com.intellij.ui.components.JBTextArea; import com.intellij.util.ui.JBFont; import com.redhat.devtools.lsp4ij.LanguageServerBundle; +import javax.swing.border.Border; +import java.util.ArrayList; +import java.util.List; + /** - * Command line widget used to fill the command to start a language server. + * Command line widget used to fill the command to start a language + * server when creating a new or modifying an existing LS configuration */ -public class CommandLineWidget extends JBTextArea { +public class CommandLineWidget extends JBTextArea implements ValidatableConsoleWidget { + private final String errorMessage = LanguageServerBundle.message("new.language.server.dialog.validation.commandLine.must.be.set"); + private final transient Border normalBorder; public CommandLineWidget() { super(5, 0); @@ -28,5 +37,22 @@ public CommandLineWidget() { super.setWrapStyleWord(true); super.setFont(JBFont.regular()); super.getEmptyText().setText(LanguageServerBundle.message("language.server.command.emptyText")); + this.normalBorder = this.getBorder(); + } + + @Override + public void validate(List validations) { + List widgetValidations = new ArrayList<>(); + + if (getDocument() != null && getText().isBlank()) { + widgetValidations.add(new ValidationInfo(errorMessage, this)); + } + + if (widgetValidations.isEmpty()) { + this.setBorder(normalBorder); + } else { + validations.addAll(widgetValidations); + setErrorBorder(this); + } } -} +} \ No newline at end of file diff --git a/src/main/java/com/redhat/devtools/lsp4ij/settings/ui/LanguageServerPanel.java b/src/main/java/com/redhat/devtools/lsp4ij/settings/ui/LanguageServerPanel.java index a7660f94e..24abe38aa 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/settings/ui/LanguageServerPanel.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/settings/ui/LanguageServerPanel.java @@ -7,19 +7,18 @@ * * Contributors: * Red Hat, Inc. - initial API and implementation + * Mitja Leino - Add DialogWrapper for validations ******************************************************************************/ package com.redhat.devtools.lsp4ij.settings.ui; import com.intellij.execution.configuration.EnvironmentVariablesComponent; import com.intellij.ide.BrowserUtil; import com.intellij.openapi.ui.ComboBox; -import com.intellij.ui.ContextHelpLabel; -import com.intellij.ui.PortField; -import com.intellij.ui.SimpleListCellRenderer; +import com.intellij.openapi.ui.ValidationInfo; +import com.intellij.ui.*; import com.intellij.ui.components.JBCheckBox; import com.intellij.ui.components.JBScrollPane; import com.intellij.ui.components.JBTabbedPane; -import com.intellij.ui.components.JBTextField; import com.intellij.ui.scale.JBUIScale; import com.intellij.util.ui.FormBuilder; import com.intellij.util.ui.components.BorderLayoutPanel; @@ -29,7 +28,11 @@ import org.jetbrains.annotations.NotNull; import javax.swing.*; +import javax.swing.event.DocumentEvent; +import javax.swing.text.JTextComponent; import java.awt.*; +import java.util.ArrayList; +import java.util.List; /** * Language server panel which show information about language server in several tabs: @@ -49,7 +52,7 @@ public enum EditionMode { EDIT_EXTENSION; } - private JBTextField serverName; + private ServerNameWidget serverName; private EnvironmentVariablesComponent environmentVariables; private CommandLineWidget commandLine; private ServerMappingsPanel mappingsPanel; @@ -61,8 +64,10 @@ public enum EditionMode { private LanguageServerConfigurationWidget configurationWidget; private LanguageServerInitializationOptionsWidget initializationOptionsWidget; + private final ValidatableDialog dialogWrapper; - public LanguageServerPanel(FormBuilder builder, JComponent description, EditionMode mode) { + public LanguageServerPanel(FormBuilder builder, JComponent description, EditionMode mode, ValidatableDialog dialogWrapper) { + this.dialogWrapper = dialogWrapper; createUI(builder, description, mode); } @@ -80,6 +85,16 @@ private void createUI(FormBuilder builder, JComponent description, EditionMode m } // Debug tab addDebugTab(tabbedPane, mode); + + // Add validation + var serverNameWidget = getServerName(); + if (serverNameWidget != null) { + addValidator(serverNameWidget); + } + var commandLineWidget = getCommandLine(); + if (commandLineWidget != null) { + addValidator(getCommandLine()); + } } private void addServerTab(JBTabbedPane tabbedPane, JComponent description, EditionMode mode) { @@ -176,7 +191,7 @@ private static FormBuilder addTab(JBTabbedPane tabbedPane, String tabTitle, bool } private void createServerNameField(FormBuilder builder) { - serverName = new JBTextField(); + serverName = new ServerNameWidget(); builder.addLabeledComponent(LanguageServerBundle.message("language.server.serverName"), serverName); } @@ -201,7 +216,7 @@ private void createInitializationOptionsTabField(FormBuilder builder) { builder.addLabeledComponent(LanguageServerBundle.message("language.server.initializationOptions"), scrollPane, true); } - public JBTextField getServerName() { + public ServerNameWidget getServerName() { return serverName; } @@ -241,4 +256,25 @@ public ComboBox getErrorReportingKindCombo() { return errorReportingKindCombo; } + public @NotNull List doValidateAll() { + List validations = new ArrayList<>(); + var serverNameWidget = getServerName(); + if (serverNameWidget != null) { + serverNameWidget.validate(validations); + } + var commandLineWidget = getCommandLine(); + if (commandLineWidget != null) { + commandLineWidget.validate(validations); + } + return validations; + } + + private void addValidator(JTextComponent textComponent) { + textComponent.getDocument().addDocumentListener(new DocumentAdapter() { + @Override + protected void textChanged(@NotNull DocumentEvent e) { + dialogWrapper.refreshValidation(); + } + }); + } } diff --git a/src/main/java/com/redhat/devtools/lsp4ij/settings/ui/ServerNameWidget.java b/src/main/java/com/redhat/devtools/lsp4ij/settings/ui/ServerNameWidget.java new file mode 100644 index 000000000..6b2d3c089 --- /dev/null +++ b/src/main/java/com/redhat/devtools/lsp4ij/settings/ui/ServerNameWidget.java @@ -0,0 +1,53 @@ +/******************************************************************************* + * Copyright (c) 2024 Red Hat Inc. and others. + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Mitja Leino - Initial API and implementation + *******************************************************************************/ +package com.redhat.devtools.lsp4ij.settings.ui; + +import com.intellij.openapi.ui.ValidationInfo; +import com.intellij.ui.components.JBTextField; +import com.redhat.devtools.lsp4ij.LanguageServerBundle; + +import javax.swing.border.Border; +import java.util.ArrayList; +import java.util.List; + +/** + * Server name widget that contains the server name when creating a new LS configuration + */ +public class ServerNameWidget extends JBTextField implements ValidatableConsoleWidget { + private final String errorMessage = LanguageServerBundle.message("new.language.server.dialog.validation.serverName.must.be.set"); + private final transient Border originalBorder; + + public ServerNameWidget() { + this.originalBorder = this.getBorder(); + } + + @Override + public void validate(List validations) { + List widgetValidations = new ArrayList<>(); + + if (getDocument() != null && getText().isBlank()) { + widgetValidations.add(new ValidationInfo(errorMessage, this)); + } + + if (widgetValidations.isEmpty()) { + this.setBorder(originalBorder); + } else { + validations.addAll(widgetValidations); + setErrorBorder(this); + } + } + + @Override + public boolean isValid() { + return getDocument() != null && !getText().isBlank(); + } +} \ No newline at end of file diff --git a/src/main/java/com/redhat/devtools/lsp4ij/settings/ui/ValidatableConsoleWidget.java b/src/main/java/com/redhat/devtools/lsp4ij/settings/ui/ValidatableConsoleWidget.java new file mode 100644 index 000000000..ab815af39 --- /dev/null +++ b/src/main/java/com/redhat/devtools/lsp4ij/settings/ui/ValidatableConsoleWidget.java @@ -0,0 +1,43 @@ +/******************************************************************************* + * Copyright (c) 2024 Red Hat Inc. and others. + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Mitja Leino - Initial API and implementation + *******************************************************************************/ +package com.redhat.devtools.lsp4ij.settings.ui; + +import com.intellij.openapi.ui.ValidationInfo; +import com.intellij.ui.JBColor; +import com.intellij.util.ui.JBUI; + +import javax.swing.*; +import java.util.List; +import java.awt.*; + +/** + * A shared interface meant to simplify creating validatable components used in + * NewLanguageServerDialog and LanguageServerView (LSP console) + */ +public interface ValidatableConsoleWidget { + /** + * Set a common error border for the widget + * @param jComponent interface implementor (e.g. setErrorBorder(this);) + */ + default void setErrorBorder(JComponent jComponent) { + Color color = JBColor.red; + color = color.darker(); + jComponent.setBorder(JBUI.Borders.customLine(color, 1)); + } + + /** + * Runs validations on the widget and handles border styling + * @param validations the dialog wrapper validation list, + * adds the validations to the list if there are any errors + */ + void validate(List validations); +} \ No newline at end of file diff --git a/src/main/java/com/redhat/devtools/lsp4ij/settings/ui/ValidatableDialog.java b/src/main/java/com/redhat/devtools/lsp4ij/settings/ui/ValidatableDialog.java new file mode 100644 index 000000000..c9852f892 --- /dev/null +++ b/src/main/java/com/redhat/devtools/lsp4ij/settings/ui/ValidatableDialog.java @@ -0,0 +1,33 @@ +/******************************************************************************* + * Copyright (c) 2024 Red Hat Inc. and others. + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Mitja Leino - Initial API and implementation + *******************************************************************************/ +package com.redhat.devtools.lsp4ij.settings.ui; + +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.DialogWrapper; + +/** + * Shareable component for shared validations using DialogWrapper + */ +public abstract class ValidatableDialog extends DialogWrapper { + protected ValidatableDialog(Project project) { + super(project); + } + + public void refreshValidation() { + super.initValidation(); + } + + @Override + protected boolean continuousValidation() { + return false; + } +}