diff --git a/docs/LSPApi.md b/docs/LSPApi.md index 0d33af1cb..5f0b76169 100644 --- a/docs/LSPApi.md +++ b/docs/LSPApi.md @@ -228,6 +228,7 @@ public class MyLSPCodeLensFeature extends LSPCodeLensFeature { | boolean isStrikeout(CompletionItem item) | Returns true if the IntelliJ lookup is strike out and false otherwise. | use `item.getDeprecated()` or `item.getTags().contains(CompletionItemTag.Deprecated)` | | String getTailText(CompletionItem item) | Returns the IntelliJ lookup tail text from the given LSP completion item and null otherwise. | `item.getLabelDetails().getDetail()` | | boolean isItemTextBold(CompletionItem item) | Returns the IntelliJ lookup item text bold from the given LSP completion item and null otherwise. | `item.getKind() == CompletionItemKind.Keyword` | +| boolean isCaseSensitive(PsiFile file) | Determines whether or not completions should be offered in a case-sensitive manner. | Case-insensitive. | ## LSP Declaration Feature diff --git a/src/main/java/com/redhat/devtools/lsp4ij/LanguageServersRegistry.java b/src/main/java/com/redhat/devtools/lsp4ij/LanguageServersRegistry.java index b84316837..55bf7dcf1 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/LanguageServersRegistry.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/LanguageServersRegistry.java @@ -162,7 +162,8 @@ private void loadServersAndMappingFromSettings() { launch.getUserEnvironmentVariables(), launch.isIncludeSystemEnvironmentVariables(), launch.getConfigurationContent(), - launch.getInitializationOptionsContent()), + launch.getInitializationOptionsContent(), + launch.getClientConfigurationContent()), mappings); } } catch (Exception e) { @@ -365,6 +366,7 @@ private void addServerDefinitionWithoutNotification(@NotNull LanguageServerDefin } settings.setConfigurationContent(definitionFromSettings.getConfigurationContent()); settings.setInitializationOptionsContent(definitionFromSettings.getInitializationOptionsContent()); + settings.setClientConfigurationContent(definitionFromSettings.getClientConfigurationContent()); UserDefinedLanguageServerSettings.getInstance().setLaunchConfigSettings(languageServerId, settings); } } @@ -469,6 +471,7 @@ private void removeAssociationsFor(LanguageServerDefinition definition) { request.serverDefinition().setIncludeSystemEnvironmentVariables(request.includeSystemEnvironmentVariables()); request.serverDefinition().setConfigurationContent(request.configurationContent()); request.serverDefinition().setInitializationOptionsContent(request.initializationOptionsContent()); + request.serverDefinition().setClientConfigurationContent(request.clientConfigurationContent()); // remove associations removeAssociationsFor(request.serverDefinition()); @@ -483,6 +486,7 @@ private void removeAssociationsFor(LanguageServerDefinition definition) { boolean mappingsChanged = !Objects.deepEquals(settings.getMappings(), request.mappings()); boolean configurationContentChanged = !Objects.equals(settings.getConfigurationContent(), request.configurationContent()); boolean initializationOptionsContentChanged = !Objects.equals(settings.getInitializationOptionsContent(), request.initializationOptionsContent()); + // Not checking whether client config changed because that shouldn't result in a LanguageServerChangedEvent settings.setServerName(request.name()); settings.setCommandLine(request.commandLine()); @@ -490,6 +494,7 @@ private void removeAssociationsFor(LanguageServerDefinition definition) { settings.setIncludeSystemEnvironmentVariables(request.includeSystemEnvironmentVariables()); settings.setConfigurationContent(request.configurationContent()); settings.setInitializationOptionsContent(request.initializationOptionsContent()); + settings.setClientConfigurationContent(request.clientConfigurationContent); settings.setMappings(request.mappings()); if (nameChanged || commandChanged || userEnvironmentVariablesChanged || includeSystemEnvironmentVariablesChanged || @@ -578,7 +583,8 @@ public record UpdateServerDefinitionRequest(@NotNull Project project, boolean includeSystemEnvironmentVariables, @NotNull List mappings, @Nullable String configurationContent, - @Nullable String initializationOptionsContent) { + @Nullable String initializationOptionsContent, + @Nullable String clientConfigurationContent) { } /** diff --git a/src/main/java/com/redhat/devtools/lsp4ij/client/features/LSPCompletionFeature.java b/src/main/java/com/redhat/devtools/lsp4ij/client/features/LSPCompletionFeature.java index efb44af1f..fb7e01afa 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/client/features/LSPCompletionFeature.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/client/features/LSPCompletionFeature.java @@ -215,6 +215,7 @@ public boolean isItemTextBold(@NotNull CompletionItem item) { /** * Don't override this method, we need to revisit the API and the prefix computation (to customize it). * + * @param context * @param completionPrefix * @param result * @param lookupItem @@ -222,25 +223,39 @@ public boolean isItemTextBold(@NotNull CompletionItem item) { * @param item */ @ApiStatus.Internal - public void addLookupItem(@NotNull CompletionPrefix completionPrefix, + public void addLookupItem(@NotNull LSPCompletionContext context, + @NotNull CompletionPrefix completionPrefix, @NotNull CompletionResultSet result, @NotNull LookupElement lookupItem, int priority, @NotNull CompletionItem item) { + // Determine whether or not completions should be case-sensitive + boolean caseSensitive = isCaseSensitive(context.getParameters().getOriginalFile()); + var prioritizedLookupItem = PrioritizedLookupElement.withPriority(lookupItem, priority); + // Compute the prefix var textEditRange = ((LSPCompletionProposal) lookupItem).getTextEditRange(); String prefix = textEditRange != null ? completionPrefix.getPrefixFor(textEditRange, item) : null; if (prefix != null) { - // Add the IJ completion item (lookup item) by using the computed prefix - result.withPrefixMatcher(prefix) - .caseInsensitive() - .addElement(prioritizedLookupItem); + // Add the IJ completion item (lookup item) by using the computed prefix respecting the language's case-sensitivity + if (caseSensitive) { + result.withPrefixMatcher(prefix) + .addElement(prioritizedLookupItem); + } else { + result.withPrefixMatcher(prefix) + .caseInsensitive() + .addElement(prioritizedLookupItem); + } } else { // Should happen rarely, only when text edit is for multi-lines or if completion is triggered outside the text edit range. - // Add the IJ completion item (lookup item) which will use the IJ prefix - result.caseInsensitive() - .addElement(prioritizedLookupItem); + // Add the IJ completion item (lookup item) which will use the IJ prefix respecting the language's case-sensitivity + if (caseSensitive) { + result.addElement(prioritizedLookupItem); + } else { + result.caseInsensitive() + .addElement(prioritizedLookupItem); + } } } @@ -278,4 +293,15 @@ public void setServerCapabilities(@Nullable ServerCapabilities serverCapabilitie completionCapabilityRegistry.setServerCapabilities(serverCapabilities); } } + + /** + * Determines whether or not completions for the file should be offered in a case-sensitive manner. + * + * @param file the file + * @return true if completions should be offered in a case-sensitive manner; otherwise false + */ + public boolean isCaseSensitive(@NotNull PsiFile file) { + // Default to case-insensitive + return false; + } } diff --git a/src/main/java/com/redhat/devtools/lsp4ij/features/completion/LSPCompletionContributor.java b/src/main/java/com/redhat/devtools/lsp4ij/features/completion/LSPCompletionContributor.java index e2a4924b5..34cb0f0d9 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/features/completion/LSPCompletionContributor.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/features/completion/LSPCompletionContributor.java @@ -25,9 +25,9 @@ import com.redhat.devtools.lsp4ij.LSPIJUtils; import com.redhat.devtools.lsp4ij.LanguageServerItem; import com.redhat.devtools.lsp4ij.client.ExecuteLSPFeatureStatus; -import com.redhat.devtools.lsp4ij.client.indexing.ProjectIndexingManager; import com.redhat.devtools.lsp4ij.client.features.LSPCompletionFeature; import com.redhat.devtools.lsp4ij.client.features.LSPCompletionProposal; +import com.redhat.devtools.lsp4ij.client.indexing.ProjectIndexingManager; import org.eclipse.lsp4j.*; import org.eclipse.lsp4j.jsonrpc.messages.Either; import org.jetbrains.annotations.NotNull; @@ -136,7 +136,7 @@ private void addCompletionItems(@NotNull CompletionParameters parameters, // Create lookup item LookupElement lookupItem = completionFeature.createLookupElement(item, context); if (lookupItem != null) { - completionFeature.addLookupItem(completionPrefix, result, lookupItem, size- i, item); + completionFeature.addLookupItem(context, completionPrefix, result, lookupItem, size - i, item); } } } @@ -217,5 +217,4 @@ private static final String getCompletionChar(int offset, Document document) { } return null; } - } \ No newline at end of file diff --git a/src/main/java/com/redhat/devtools/lsp4ij/launching/UserDefinedLanguageServerSettings.java b/src/main/java/com/redhat/devtools/lsp4ij/launching/UserDefinedLanguageServerSettings.java index 63e047ed4..5ecd6ee10 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/launching/UserDefinedLanguageServerSettings.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/launching/UserDefinedLanguageServerSettings.java @@ -119,6 +119,8 @@ public static class UserDefinedLanguageServerItemSettings { private String initializationOptionsContent; + private String clientConfigurationContent = "{}"; + @XCollection(elementTypes = ServerMappingSettings.class) private List mappings; @@ -206,6 +208,14 @@ public String getInitializationOptionsContent() { public void setInitializationOptionsContent(String initializationOptionsContent) { this.initializationOptionsContent = initializationOptionsContent; } + + public String getClientConfigurationContent() { + return clientConfigurationContent; + } + + public void setClientConfigurationContent(String clientConfigurationContent) { + this.clientConfigurationContent = clientConfigurationContent; + } } public static class MyState { @@ -215,7 +225,5 @@ public static class MyState { MyState() { } - } - } diff --git a/src/main/java/com/redhat/devtools/lsp4ij/launching/templates/LanguageServerTemplate.java b/src/main/java/com/redhat/devtools/lsp4ij/launching/templates/LanguageServerTemplate.java index f4f20cf3c..59c7909bb 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/launching/templates/LanguageServerTemplate.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/launching/templates/LanguageServerTemplate.java @@ -40,6 +40,7 @@ public String getName() { public static final String TEMPLATE_FILE_NAME = "template.json"; public static final String INITIALIZATION_OPTIONS_FILE_NAME = "initializationOptions.json"; public static final String SETTINGS_FILE_NAME = "settings.json"; + public static final String CLIENT_SETTINGS_FILE_NAME = "clientSettings.json"; public static final String README_FILE_NAME = "README.md"; public static final String LANGUAGE_ID_JSON_PROPERTY = "languageId"; @@ -70,6 +71,7 @@ public String getName() { private String configuration; private String initializationOptions; + private String clientConfiguration; public String getName() { return name; @@ -150,4 +152,11 @@ public void setInitializationOptions(String initializationOptions) { this.initializationOptions = initializationOptions; } + public String getClientConfiguration() { + return clientConfiguration; + } + + public void setClientConfiguration(String clientConfiguration) { + this.clientConfiguration = clientConfiguration; + } } \ No newline at end of file diff --git a/src/main/java/com/redhat/devtools/lsp4ij/launching/templates/LanguageServerTemplateManager.java b/src/main/java/com/redhat/devtools/lsp4ij/launching/templates/LanguageServerTemplateManager.java index b71d4fc32..be32e3807 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/launching/templates/LanguageServerTemplateManager.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/launching/templates/LanguageServerTemplateManager.java @@ -17,25 +17,26 @@ import com.intellij.openapi.vfs.JarFileSystem; import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.openapi.vfs.VirtualFile; +import com.redhat.devtools.lsp4ij.internal.StringUtils; import com.redhat.devtools.lsp4ij.server.definition.LanguageServerDefinition; +import com.redhat.devtools.lsp4ij.server.definition.launching.UserDefinedLanguageServerDefinition; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.redhat.devtools.lsp4ij.internal.StringUtils; -import com.redhat.devtools.lsp4ij.server.definition.launching.UserDefinedLanguageServerDefinition; -import java.io.*; +import java.io.ByteArrayOutputStream; +import java.io.FileNotFoundException; +import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; -import java.net.URLDecoder; +import java.nio.charset.StandardCharsets; import java.util.AbstractMap.SimpleEntry; import java.util.ArrayList; +import java.util.List; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; -import java.nio.charset.StandardCharsets; -import java.util.List; import static com.redhat.devtools.lsp4ij.launching.templates.LanguageServerTemplate.*; @@ -82,13 +83,14 @@ private LanguageServerTemplateManager() { /** * Load the resources/templates file as a VirtualFile from the jar + * * @return template root as virtual file or null, if one couldn't be found */ @Nullable public VirtualFile getTemplateRoot() { URL url = LanguageServerTemplateManager.class.getClassLoader().getResource(TEMPLATES_DIR); if (url == null) { - LOGGER.warn("No "+TEMPLATES_DIR+ " directory/url found"); + LOGGER.warn("No " + TEMPLATES_DIR + " directory/url found"); return null; } try { @@ -96,7 +98,7 @@ public VirtualFile getTemplateRoot() { String filePart = url.toURI().getRawSchemeSpecificPart(); // get un-decoded, URI compatible part // filePart looks like file:/Users/username/Library/Application%20Support/JetBrains/IDEVersion/plugins/LSP4IJ/lib/instrumented-lsp4ij-version.jar!/templates LOGGER.debug("Templates filePart : {}", filePart); - String resourcePath = new URI(filePart).getSchemeSpecificPart();// get decoded part (i.e. converts %20 to spaces ...) + String resourcePath = new URI(filePart).getSchemeSpecificPart();// get decoded part (i.e. converts %20 to spaces ...) // resourcePath looks like /Users/username/Library/Application Support/JetBrains/IDEVersion/plugins/LSP4IJ/lib/instrumented-lsp4ij-version.jar!/templates/ LOGGER.debug("Templates resources path from uri : {}", resourcePath); return JarFileSystem.getInstance().findFileByPath(resourcePath); @@ -112,6 +114,7 @@ public List getTemplates() { /** * Import language server template from a directory + * * @param templateFolder directory that contains the template files * @return LanguageServerTemplate or null if one couldn't be created */ @@ -122,6 +125,7 @@ public LanguageServerTemplate importLsTemplate(@NotNull VirtualFile templateFold /** * Parses the template files to create a LanguageServerTemplate + * * @param templateFolder directory that contains the template files * @return LanguageServerTemplate or null if one couldn't be created * @throws IOException if an IO error occurs when loading the text from any template file @@ -131,6 +135,7 @@ public LanguageServerTemplate createLsTemplate(@NotNull VirtualFile templateFold String templateJson = null; String settingsJson = null; String initializationOptionsJson = null; + String clientSettingsJson = null; String description = null; for (VirtualFile file : templateFolder.getChildren()) { @@ -147,6 +152,9 @@ public LanguageServerTemplate createLsTemplate(@NotNull VirtualFile templateFold case INITIALIZATION_OPTIONS_FILE_NAME: initializationOptionsJson = VfsUtilCore.loadText(file); break; + case CLIENT_SETTINGS_FILE_NAME: + clientSettingsJson = VfsUtilCore.loadText(file); + break; case README_FILE_NAME: description = VfsUtilCore.loadText(file); break; @@ -165,6 +173,9 @@ public LanguageServerTemplate createLsTemplate(@NotNull VirtualFile templateFold if (initializationOptionsJson == null) { initializationOptionsJson = "{}"; } + if (clientSettingsJson == null) { + clientSettingsJson = "{}"; + } GsonBuilder builder = new GsonBuilder(); builder.registerTypeAdapter(LanguageServerTemplate.class, new LanguageServerTemplateDeserializer()); @@ -173,6 +184,7 @@ public LanguageServerTemplate createLsTemplate(@NotNull VirtualFile templateFold LanguageServerTemplate template = gson.fromJson(templateJson, LanguageServerTemplate.class); template.setConfiguration(settingsJson); template.setInitializationOptions(initializationOptionsJson); + template.setClientConfiguration(clientSettingsJson); if (StringUtils.isNotBlank(description)) { template.setDescription(description); } @@ -182,7 +194,8 @@ public LanguageServerTemplate createLsTemplate(@NotNull VirtualFile templateFold /** * Exports one or more language server templates to a zip file - * @param exportZip target zip + * + * @param exportZip target zip * @param lsDefinitions to export */ public int exportLsTemplates(@NotNull VirtualFile exportZip, @NotNull List lsDefinitions) { @@ -200,6 +213,7 @@ public int exportLsTemplates(@NotNull VirtualFile exportZip, @NotNull List createZipFromLanguageServers(@NotNull List< String template = gson.toJson(lsDefinition); String initializationOptions = ((UserDefinedLanguageServerDefinition) lsDefinition).getInitializationOptionsContent(); String settings = ((UserDefinedLanguageServerDefinition) lsDefinition).getConfigurationContent(); + String clientSettings = ((UserDefinedLanguageServerDefinition) lsDefinition).getClientConfigurationContent(); lsName = lsDefinition.getDisplayName(); writeToZip(TEMPLATE_FILE_NAME, template, zos); writeToZip(INITIALIZATION_OPTIONS_FILE_NAME, initializationOptions, zos); writeToZip(SETTINGS_FILE_NAME, settings, zos); + writeToZip(CLIENT_SETTINGS_FILE_NAME, clientSettings, zos); zos.closeEntry(); count++; } @@ -231,9 +247,10 @@ private SimpleEntry createZipFromLanguageServers(@NotNull List< /** * Writes a file (name + content) to a zip output stream + * * @param filename name of the file to write - * @param content file content - * @param zos to write the file to + * @param content file content + * @param zos to write the file to */ private void writeToZip(String filename, String content, ZipOutputStream zos) throws IOException { if (StringUtils.isBlank(content)) { 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 7f6a4f768..705b1a62a 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 @@ -16,7 +16,9 @@ import com.intellij.openapi.fileChooser.FileChooser; import com.intellij.openapi.fileChooser.FileChooserDescriptor; import com.intellij.openapi.project.Project; -import com.intellij.openapi.ui.*; +import com.intellij.openapi.ui.ComboBox; +import com.intellij.openapi.ui.DialogWrapper; +import com.intellij.openapi.ui.ValidationInfo; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.ui.DocumentAdapter; import com.intellij.ui.SimpleListCellRenderer; @@ -112,7 +114,7 @@ public void customize(@NotNull JList list, } }); - showInstructionButton = super.createHelpButton(new JBInsets(0,0,0,0)); + showInstructionButton = super.createHelpButton(new JBInsets(0, 0, 0, 0)); showInstructionButton.setText(""); templateCombo.addItemListener(getTemplateComboListener()); @@ -123,6 +125,7 @@ public void customize(@NotNull JList list, /** * Create the template combo listener that handles item selection + * * @return created ItemListener */ private ItemListener getTemplateComboListener() { @@ -169,6 +172,7 @@ private void showImportErrorNotification(String message) { /** * Check that the template is not a placeholder and that it has a valid description + * * @param template to check * @return true if template is not null, not a placeholder and has a description, else false */ @@ -205,15 +209,20 @@ private void loadFromTemplate(LanguageServerTemplate template) { var mappingsPanel = this.languageServerPanel.getMappingsPanel(); mappingsPanel.refreshMappings(template); - // Update configuration + // Update server configuration var configuration = this.languageServerPanel.getConfiguration(); configuration.setText(template.getConfiguration() != null ? template.getConfiguration() : ""); configuration.setCaretPosition(0); - // Update initialize options + // Update initialization options var initializationOptions = this.languageServerPanel.getInitializationOptionsWidget(); initializationOptions.setText(template.getInitializationOptions() != null ? template.getInitializationOptions() : ""); initializationOptions.setCaretPosition(0); + + // Update client configuration + var clientConfiguration = this.languageServerPanel.getClientConfigurationWidget(); + clientConfiguration.setText(template.getClientConfiguration() != null ? template.getClientConfiguration() : ""); + clientConfiguration.setCaretPosition(0); } private static String getCommandLine(LanguageServerTemplate entry) { @@ -285,16 +294,17 @@ protected void doOKAction() { boolean includeSystemEnvironmentVariables = this.languageServerPanel.getEnvironmentVariables().isPassParentEnvs(); String configuration = this.languageServerPanel.getConfiguration().getText(); String initializationOptions = this.languageServerPanel.getInitializationOptionsWidget().getText(); + String clientConfiguration = this.languageServerPanel.getClientConfigurationWidget().getText(); UserDefinedLanguageServerDefinition definition = new UserDefinedLanguageServerDefinition(serverId, serverName, "", commandLine, - userEnvironmentVariables , + userEnvironmentVariables, includeSystemEnvironmentVariables, configuration, - initializationOptions); + initializationOptions, + clientConfiguration); LanguageServersRegistry.getInstance().addServerDefinition(project, definition, mappingSettings); - } private void addValidator(JTextComponent textComponent) { @@ -305,5 +315,4 @@ protected void textChanged(@NotNull DocumentEvent e) { } }); } - } diff --git a/src/main/java/com/redhat/devtools/lsp4ij/server/definition/launching/ClientConfigurationSettings.java b/src/main/java/com/redhat/devtools/lsp4ij/server/definition/launching/ClientConfigurationSettings.java new file mode 100644 index 000000000..d8c56b82d --- /dev/null +++ b/src/main/java/com/redhat/devtools/lsp4ij/server/definition/launching/ClientConfigurationSettings.java @@ -0,0 +1,36 @@ +/******************************************************************************* + * 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 v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 + * which is available at https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 + * + * Contributors: + * Red Hat Inc. - initial API and implementation + *******************************************************************************/ +package com.redhat.devtools.lsp4ij.server.definition.launching; + +import org.jetbrains.annotations.NotNull; + +/** + * Client-side settings for a user-defined language server configuration. + */ +public class ClientConfigurationSettings { + /** + * Client-side code completion settings. + */ + public static class ClientConfigurationCompletionSettings { + /** + * Whether or not completions should be offered as case-sensitive. Defaults to false. + **/ + public boolean caseSensitive = false; + } + + /** + * Client-side code completion settings + */ + public @NotNull ClientConfigurationCompletionSettings completions = new ClientConfigurationCompletionSettings(); +} diff --git a/src/main/java/com/redhat/devtools/lsp4ij/server/definition/launching/UserDefinedClientFeatures.java b/src/main/java/com/redhat/devtools/lsp4ij/server/definition/launching/UserDefinedClientFeatures.java new file mode 100644 index 000000000..f51577835 --- /dev/null +++ b/src/main/java/com/redhat/devtools/lsp4ij/server/definition/launching/UserDefinedClientFeatures.java @@ -0,0 +1,28 @@ +/******************************************************************************* + * 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 v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 + * which is available at https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 + * + * Contributors: + * Red Hat Inc. - initial API and implementation + *******************************************************************************/ +package com.redhat.devtools.lsp4ij.server.definition.launching; + +import com.redhat.devtools.lsp4ij.client.features.LSPClientFeatures; + +/** + * Adds client-side configuration features. + */ +public class UserDefinedClientFeatures extends LSPClientFeatures { + + public UserDefinedClientFeatures() { + super(); + // Add our completion feature + setCompletionFeature(new UserDefinedCompletionFeature()); + } +} diff --git a/src/main/java/com/redhat/devtools/lsp4ij/server/definition/launching/UserDefinedCompletionFeature.java b/src/main/java/com/redhat/devtools/lsp4ij/server/definition/launching/UserDefinedCompletionFeature.java new file mode 100644 index 000000000..bf5ed1dea --- /dev/null +++ b/src/main/java/com/redhat/devtools/lsp4ij/server/definition/launching/UserDefinedCompletionFeature.java @@ -0,0 +1,34 @@ +/******************************************************************************* + * 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 v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 + * which is available at https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 + * + * Contributors: + * Red Hat Inc. - initial API and implementation + *******************************************************************************/ +package com.redhat.devtools.lsp4ij.server.definition.launching; + +import com.intellij.psi.PsiFile; +import com.redhat.devtools.lsp4ij.client.features.LSPCompletionFeature; +import com.redhat.devtools.lsp4ij.server.definition.LanguageServerDefinition; +import org.jetbrains.annotations.NotNull; + +/** + * Adds client-side completion configuration features. + */ +public class UserDefinedCompletionFeature extends LSPCompletionFeature { + public boolean isCaseSensitive(@NotNull PsiFile file) { + LanguageServerDefinition serverDefinition = getClientFeatures().getServerDefinition(); + if (serverDefinition instanceof UserDefinedLanguageServerDefinition languageServerDefinition) { + ClientConfigurationSettings clientConfiguration = languageServerDefinition.getLanguageServerClientConfiguration(); + return (clientConfiguration != null) && clientConfiguration.completions.caseSensitive; + } + // Default to case-insensitive if unspecified for backward-compatibility + return false; + } +} diff --git a/src/main/java/com/redhat/devtools/lsp4ij/server/definition/launching/UserDefinedLanguageServerDefinition.java b/src/main/java/com/redhat/devtools/lsp4ij/server/definition/launching/UserDefinedLanguageServerDefinition.java index a7d50b676..eb6622f18 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/server/definition/launching/UserDefinedLanguageServerDefinition.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/server/definition/launching/UserDefinedLanguageServerDefinition.java @@ -17,7 +17,9 @@ import com.google.gson.annotations.SerializedName; import com.intellij.execution.util.ProgramParametersUtil; import com.intellij.openapi.project.Project; +import com.redhat.devtools.lsp4ij.JSONUtils; import com.redhat.devtools.lsp4ij.client.LanguageClientImpl; +import com.redhat.devtools.lsp4ij.client.features.LSPClientFeatures; import com.redhat.devtools.lsp4ij.server.StreamConnectionProvider; import com.redhat.devtools.lsp4ij.server.definition.LanguageServerDefinition; import org.jetbrains.annotations.NotNull; @@ -45,6 +47,8 @@ public class UserDefinedLanguageServerDefinition extends LanguageServerDefinitio private Object configuration; private String initializationOptionsContent; private Object initializationOptions; + private String clientConfigurationContent; + private ClientConfigurationSettings clientConfiguration; public UserDefinedLanguageServerDefinition(@NotNull String id, @NotNull String name, @@ -53,7 +57,8 @@ public UserDefinedLanguageServerDefinition(@NotNull String id, @NotNull Map userEnvironmentVariables, boolean includeSystemEnvironmentVariables, @Nullable String configurationContent, - @Nullable String initializationOptionsContent) { + @Nullable String initializationOptionsContent, + @Nullable String clientConfigurationContent) { super(id, name, description, true, null, false); this.name = name; this.commandLine = commandLine; @@ -61,6 +66,7 @@ public UserDefinedLanguageServerDefinition(@NotNull String id, this.includeSystemEnvironmentVariables = includeSystemEnvironmentVariables; this.configurationContent = configurationContent; this.initializationOptionsContent = initializationOptionsContent; + this.clientConfigurationContent = clientConfigurationContent; } @Override @@ -89,6 +95,11 @@ public static String resolveCommandLine(@NotNull String commandLine, @NotNull Pr return new UserDefinedLanguageClient(this, project); } + @Override + public @NotNull LSPClientFeatures createClientFeatures() { + return new UserDefinedClientFeatures(); + } + public void setName(String name) { this.name = name; } @@ -135,6 +146,15 @@ public void setInitializationOptionsContent(String initializationOptionsContent) this.initializationOptions = null; } + public String getClientConfigurationContent() { + return clientConfigurationContent; + } + + public void setClientConfigurationContent(String clientConfigurationContent) { + this.clientConfigurationContent = clientConfigurationContent; + this.clientConfiguration = null; + } + public Object getLanguageServerConfiguration() { if (configuration == null && configurationContent != null && !configurationContent.isBlank()) { try { @@ -157,9 +177,19 @@ public Object getLanguageServerInitializationOptions() { return initializationOptions; } + public ClientConfigurationSettings getLanguageServerClientConfiguration() { + if ((clientConfiguration == null) && (clientConfigurationContent != null) && !clientConfigurationContent.isBlank()) { + try { + clientConfiguration = JSONUtils.getLsp4jGson().fromJson(clientConfigurationContent, ClientConfigurationSettings.class); + } catch (Exception e) { + LOGGER.error("Error while parsing JSON client configuration for the language server '" + getId() + "'", e); + } + } + return clientConfiguration; + } + @Override public @NotNull String getDisplayName() { return name; } - } 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 2f636f528..af872a8db 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/settings/LanguageServerView.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/settings/LanguageServerView.java @@ -103,7 +103,8 @@ && isEquals(this.getCommandLine(), settings.getCommandLine()) && this.getEnvData().isPassParentEnvs() == settings.isIncludeSystemEnvironmentVariables() && Objects.equals(this.getMappings(), settings.getMappings()) && isEquals(this.getConfigurationContent(), settings.getConfigurationContent()) - && isEquals(this.getInitializationOptionsContent(), settings.getInitializationOptionsContent()))){ + && isEquals(this.getInitializationOptionsContent(), settings.getInitializationOptionsContent()) + && isEquals(this.getClientConfigurationContent(), settings.getClientConfigurationContent()))) { return true; } } @@ -171,6 +172,7 @@ public void reset() { userDefinedLanguageServerSettings.isIncludeSystemEnvironmentVariables())); this.setConfigurationContent(userDefinedLanguageServerSettings.getConfigurationContent()); this.setInitializationOptionsContent(userDefinedLanguageServerSettings.getInitializationOptionsContent()); + this.setClientConfigurationContent(userDefinedLanguageServerSettings.getClientConfigurationContent()); List languageMappings = userDefinedLanguageServerSettings.getMappings() .stream() @@ -254,7 +256,7 @@ public void apply() { // Update user-defined language server settings var serverChangedEvent = LanguageServersRegistry.getInstance() .updateServerDefinition( - new LanguageServersRegistry.UpdateServerDefinitionRequest(project, launch, getDisplayName(), getCommandLine(), getEnvData().getEnvs(), getEnvData().isPassParentEnvs(), getMappings(), getConfigurationContent(), getInitializationOptionsContent()), false); + new LanguageServersRegistry.UpdateServerDefinitionRequest(project, launch, getDisplayName(), getCommandLine(), getEnvData().getEnvs(), getEnvData().isPassParentEnvs(), getMappings(), getConfigurationContent(), getInitializationOptionsContent(), getClientConfigurationContent()), false); if (settingsChangedEvent != null) { // Settings has changed, fire the event com.redhat.devtools.lsp4ij.settings.UserDefinedLanguageServerSettings @@ -408,6 +410,16 @@ public void setInitializationOptionsContent(String initializationOptionsContent) initializationOptions.setCaretPosition(0); } + public String getClientConfigurationContent() { + return languageServerPanel.getClientConfigurationWidget().getText(); + } + + public void setClientConfigurationContent(String configurationContent) { + var clientConfiguration = languageServerPanel.getClientConfigurationWidget(); + clientConfiguration.setText(configurationContent); + clientConfiguration.setCaretPosition(0); + } + @Override public void dispose() { } diff --git a/src/main/java/com/redhat/devtools/lsp4ij/settings/jsonSchema/AbstractLSPJsonSchemaFileProvider.java b/src/main/java/com/redhat/devtools/lsp4ij/settings/jsonSchema/AbstractLSPJsonSchemaFileProvider.java new file mode 100644 index 000000000..131f4e2fd --- /dev/null +++ b/src/main/java/com/redhat/devtools/lsp4ij/settings/jsonSchema/AbstractLSPJsonSchemaFileProvider.java @@ -0,0 +1,83 @@ +/******************************************************************************* + * 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 v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 + * which is available at https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 + * + * Contributors: + * Red Hat Inc. - initial API and implementation + *******************************************************************************/ +package com.redhat.devtools.lsp4ij.settings.jsonSchema; + +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vfs.VfsUtil; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.openapi.vfs.VirtualFileManager; +import com.jetbrains.jsonSchema.extension.JsonSchemaFileProvider; +import com.jetbrains.jsonSchema.extension.SchemaType; +import com.jetbrains.jsonSchema.impl.JsonSchemaVersion; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.net.URL; + +/** + * Abstract base class for JSON schema file providers that are based on JSON schema files bundled in the plugin distribution. + */ +abstract class AbstractLSPJsonSchemaFileProvider implements JsonSchemaFileProvider { + private final String jsonSchemaPath; + private final String jsonFilename; + private VirtualFile jsonSchemaFile = null; + + protected AbstractLSPJsonSchemaFileProvider(@NotNull String jsonSchemaPath, @NotNull String jsonFilename) { + this.jsonSchemaPath = jsonSchemaPath; + this.jsonFilename = jsonFilename; + } + + @Nullable + @Override + public final VirtualFile getSchemaFile() { + if (jsonSchemaFile == null) { + URL jsonSchemaUrl = getClass().getResource(jsonSchemaPath); + String jsonSchemaFileUrl = jsonSchemaUrl != null ? VfsUtil.convertFromUrl(jsonSchemaUrl) : null; + jsonSchemaFile = jsonSchemaFileUrl != null ? VirtualFileManager.getInstance().findFileByUrl(jsonSchemaFileUrl) : null; + // Make sure that the IDE is using the absolute latest version of the JSON schema + if (jsonSchemaFile != null) { + jsonSchemaFile.refresh(true, false); + } + } + return jsonSchemaFile; + } + + @Override + public boolean isAvailable(@NotNull VirtualFile file) { + return StringUtil.equalsIgnoreCase(jsonFilename, file.getName()); + } + + @NotNull + @Override + public final String getName() { + return jsonFilename; + } + + @NotNull + @Override + public final SchemaType getSchemaType() { + return SchemaType.schema; + } + + @Override + public final JsonSchemaVersion getSchemaVersion() { + return JsonSchemaVersion.SCHEMA_7; + } + + @NotNull + @Override + public final String getPresentableName() { + return getName(); + } +} diff --git a/src/main/java/com/redhat/devtools/lsp4ij/settings/jsonSchema/LSPClientConfigurationJsonSchemaFileProvider.java b/src/main/java/com/redhat/devtools/lsp4ij/settings/jsonSchema/LSPClientConfigurationJsonSchemaFileProvider.java new file mode 100644 index 000000000..e4b300b55 --- /dev/null +++ b/src/main/java/com/redhat/devtools/lsp4ij/settings/jsonSchema/LSPClientConfigurationJsonSchemaFileProvider.java @@ -0,0 +1,26 @@ +/******************************************************************************* + * 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 v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 + * which is available at https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 + * + * Contributors: + * Red Hat Inc. - initial API and implementation + *******************************************************************************/ +package com.redhat.devtools.lsp4ij.settings.jsonSchema; + +/** + * JSON schema file provider for language server client-side configuration. + */ +public class LSPClientConfigurationJsonSchemaFileProvider extends AbstractLSPJsonSchemaFileProvider { + private static final String CLIENT_SETTINGS_SCHEMA_JSON_PATH = "/jsonSchema/clientSettings.schema.json"; + public static final String CLIENT_SETTINGS_JSON_FILE_NAME = "clientSettings.json"; + + LSPClientConfigurationJsonSchemaFileProvider() { + super(CLIENT_SETTINGS_SCHEMA_JSON_PATH, CLIENT_SETTINGS_JSON_FILE_NAME); + } +} diff --git a/src/main/java/com/redhat/devtools/lsp4ij/settings/jsonSchema/LSPJsonSchemaProviderFactory.java b/src/main/java/com/redhat/devtools/lsp4ij/settings/jsonSchema/LSPJsonSchemaProviderFactory.java new file mode 100644 index 000000000..ce32cc04b --- /dev/null +++ b/src/main/java/com/redhat/devtools/lsp4ij/settings/jsonSchema/LSPJsonSchemaProviderFactory.java @@ -0,0 +1,34 @@ +/******************************************************************************* + * 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 v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 + * which is available at https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 + * + * Contributors: + * Red Hat Inc. - initial API and implementation + *******************************************************************************/ +package com.redhat.devtools.lsp4ij.settings.jsonSchema; + +import com.intellij.openapi.project.Project; +import com.jetbrains.jsonSchema.extension.JsonSchemaFileProvider; +import com.jetbrains.jsonSchema.extension.JsonSchemaProviderFactory; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +/** + * Factory for JSON schema file providers that are based on JSON schema files bundled in the plugin distribution. + */ +public class LSPJsonSchemaProviderFactory implements JsonSchemaProviderFactory { + @NotNull + @Override + public List getProviders(@NotNull Project project) { + return List.of( + new LSPClientConfigurationJsonSchemaFileProvider() + ); + } +} diff --git a/src/main/java/com/redhat/devtools/lsp4ij/settings/ui/JsonTextField.java b/src/main/java/com/redhat/devtools/lsp4ij/settings/ui/JsonTextField.java index a8231e58d..2c7e0964d 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/settings/ui/JsonTextField.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/settings/ui/JsonTextField.java @@ -12,15 +12,21 @@ import com.intellij.lang.Language; import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.SpellCheckingEditorCustomizationProvider; +import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.fileTypes.PlainTextFileType; import com.intellij.openapi.project.Project; +import com.intellij.openapi.vfs.VirtualFile; import com.intellij.ui.*; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import javax.swing.*; import java.awt.*; +import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -30,6 +36,8 @@ */ public class JsonTextField extends JPanel { + private static final Logger LOGGER = LoggerFactory.getLogger(JsonTextField.class); + private static final String JSON_LANGUAGE_NAME = "JSON"; private static final String DEFAULT_VALUE = "{}"; @@ -57,6 +65,25 @@ public JsonTextField(@NotNull Project project) { add(editorTextField, BorderLayout.CENTER); } + /** + * Sets the editor's filename so that the correct JSON schema will be used for code completion and validation. + * + * @param jsonFilename the JSON file name + */ + public void setJsonFilename(@NotNull String jsonFilename) { + try { + Document document = editorTextField.getDocument(); + VirtualFile file = FileDocumentManager.getInstance().getFile(document); + if (file != null) { + file.rename(this, jsonFilename); + } else { + LOGGER.warn("Failed to rename the JSON text field file to '{}'.", jsonFilename); + } + } catch (IOException e) { + LOGGER.warn("Failed to configure JSON text field for JSON schema '{}'.", jsonFilename, e); + } + } + // Proxy some simple accessors to the editor text field public void setText(@NotNull String text) { diff --git a/src/main/java/com/redhat/devtools/lsp4ij/settings/ui/LanguageServerConfigurationWidget.java b/src/main/java/com/redhat/devtools/lsp4ij/settings/ui/LanguageServerConfigurationWidget.java deleted file mode 100644 index a515322f7..000000000 --- a/src/main/java/com/redhat/devtools/lsp4ij/settings/ui/LanguageServerConfigurationWidget.java +++ /dev/null @@ -1,23 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2024 Red Hat, Inc. - * Distributed under license by Red Hat, Inc. All rights reserved. - * This program is made available under the terms of the - * Eclipse Public License v2.0 which accompanies this distribution, - * and is available at https://www.eclipse.org/legal/epl-v20.html - * - * Contributors: - * Red Hat, Inc. - initial API and implementation - ******************************************************************************/ -package com.redhat.devtools.lsp4ij.settings.ui; - -import com.intellij.openapi.project.Project; -import org.jetbrains.annotations.NotNull; - -/** - * Language server configuration widget used to fill the configuration expected by the language server. - */ -public class LanguageServerConfigurationWidget extends JsonTextField { - public LanguageServerConfigurationWidget(@NotNull Project project) { - super(project); - } -} diff --git a/src/main/java/com/redhat/devtools/lsp4ij/settings/ui/LanguageServerInitializationOptionsWidget.java b/src/main/java/com/redhat/devtools/lsp4ij/settings/ui/LanguageServerInitializationOptionsWidget.java deleted file mode 100644 index 0e8d89d53..000000000 --- a/src/main/java/com/redhat/devtools/lsp4ij/settings/ui/LanguageServerInitializationOptionsWidget.java +++ /dev/null @@ -1,23 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2024 Red Hat, Inc. - * Distributed under license by Red Hat, Inc. All rights reserved. - * This program is made available under the terms of the - * Eclipse Public License v2.0 which accompanies this distribution, - * and is available at https://www.eclipse.org/legal/epl-v20.html - * - * Contributors: - * Red Hat, Inc. - initial API and implementation - ******************************************************************************/ -package com.redhat.devtools.lsp4ij.settings.ui; - -import com.intellij.openapi.project.Project; -import org.jetbrains.annotations.NotNull; - -/** - * Language server LSP 'Initialize Options' widget used to fill the LSP 'Initialization Options' expected by the language server. - */ -public class LanguageServerInitializationOptionsWidget extends JsonTextField { - public LanguageServerInitializationOptionsWidget(@NotNull Project project) { - super(project); - } -} 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 eae9b8bd3..66c03676e 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 @@ -30,6 +30,7 @@ import com.redhat.devtools.lsp4ij.server.definition.launching.UserDefinedLanguageServerDefinition; import com.redhat.devtools.lsp4ij.settings.ErrorReportingKind; import com.redhat.devtools.lsp4ij.settings.ServerTrace; +import com.redhat.devtools.lsp4ij.settings.jsonSchema.LSPClientConfigurationJsonSchemaFileProvider; import org.jetbrains.annotations.NotNull; import javax.swing.*; @@ -68,9 +69,10 @@ public enum EditionMode { private final ComboBox serverTraceComboBox = new ComboBox<>(new DefaultComboBoxModel<>(ServerTrace.values())); private final PortField debugPortField = new PortField(); private final JBCheckBox debugSuspendCheckBox = new JBCheckBox(LanguageServerBundle.message("language.server.debug.suspend")); - private LanguageServerConfigurationWidget configurationWidget; - private LanguageServerInitializationOptionsWidget initializationOptionsWidget; + private JsonTextField configurationWidget; + private JsonTextField initializationOptionsWidget; + private JsonTextField clientConfigurationWidget; public LanguageServerPanel(FormBuilder builder, JComponent description, EditionMode mode, Project project) { this.project = project; @@ -165,8 +167,16 @@ private void createErrorReportingCombo(FormBuilder builder) { private void addConfigurationTab(JBTabbedPane tabbedPane) { FormBuilder configurationTab = addTab(tabbedPane, LanguageServerBundle.message("language.server.tab.configuration"), false); - createConfigurationField(configurationTab); - createInitializationOptionsTabField(configurationTab); + + JBTabbedPane configurationTabbedPane = new JBTabbedPane(); + configurationTab.addComponentFillVertically(configurationTabbedPane, 0); + + FormBuilder serverConfigurationTab = addTab(configurationTabbedPane, LanguageServerBundle.message("language.server.tab.configuration.server"), false); + createConfigurationField(serverConfigurationTab); + createInitializationOptionsTabField(serverConfigurationTab); + + FormBuilder clientConfigurationTab = addTab(configurationTabbedPane, LanguageServerBundle.message("language.server.tab.configuration.client"), false); + createClientConfigurationField(clientConfigurationTab); } private static FormBuilder addTab(JBTabbedPane tabbedPane, String tabTitle) { @@ -250,15 +260,21 @@ private static JLabel createLabelForComponent(@NotNull @NlsContexts.Label String } private void createConfigurationField(FormBuilder builder) { - configurationWidget = new LanguageServerConfigurationWidget(project); + configurationWidget = new JsonTextField(project); builder.addLabeledComponentFillVertically(LanguageServerBundle.message("language.server.configuration"), configurationWidget); } private void createInitializationOptionsTabField(FormBuilder builder) { - initializationOptionsWidget = new LanguageServerInitializationOptionsWidget(project); + initializationOptionsWidget = new JsonTextField(project); builder.addLabeledComponentFillVertically(LanguageServerBundle.message("language.server.initializationOptions"), initializationOptionsWidget); } + private void createClientConfigurationField(FormBuilder builder) { + clientConfigurationWidget = new JsonTextField(project); + clientConfigurationWidget.setJsonFilename(LSPClientConfigurationJsonSchemaFileProvider.CLIENT_SETTINGS_JSON_FILE_NAME); + builder.addLabeledComponentFillVertically(LanguageServerBundle.message("language.server.configuration"), clientConfigurationWidget); + } + public JBTextField getServerName() { return serverName; } @@ -275,14 +291,19 @@ public ServerMappingsPanel getMappingsPanel() { return mappingsPanel; } - public LanguageServerConfigurationWidget getConfiguration() { + // TODO: Rename this to getConfigurationWidget()? getServerConfigurationWidget()? + public JsonTextField getConfiguration() { return configurationWidget; } - public LanguageServerInitializationOptionsWidget getInitializationOptionsWidget() { + public JsonTextField getInitializationOptionsWidget() { return initializationOptionsWidget; } + public JsonTextField getClientConfigurationWidget() { + return clientConfigurationWidget; + } + public JBCheckBox getDebugSuspendCheckBox() { return debugSuspendCheckBox; } @@ -298,5 +319,4 @@ public ComboBox getServerTraceComboBox() { public ComboBox getErrorReportingKindCombo() { return errorReportingKindCombo; } - } diff --git a/src/main/resources/META-INF/plugin-json.xml b/src/main/resources/META-INF/plugin-json.xml new file mode 100644 index 000000000..c6e15a97f --- /dev/null +++ b/src/main/resources/META-INF/plugin-json.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index 099564004..d78d4e3bf 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -158,6 +158,7 @@ com.intellij.modules.platform org.jetbrains.plugins.textmate com.redhat.devtools.intellij.telemetry + com.intellij.modules.json diff --git a/src/main/resources/jsonSchema/clientSettings.schema.json b/src/main/resources/jsonSchema/clientSettings.schema.json new file mode 100644 index 000000000..2196ebaee --- /dev/null +++ b/src/main/resources/jsonSchema/clientSettings.schema.json @@ -0,0 +1,22 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://github.com/redhat-developer/lsp4ij/tree/main/src/main/resources/jsonSchema/clientSettings.schema.json", + "title": "LSP4IJ user-defined language server client-side settings JSON schema", + "type": "object", + "additionalProperties": false, + "properties": { + "completions": { + "type": "object", + "title": "Client-side completion configuration", + "additionalProperties": false, + "properties": { + "caseSensitive": { + "type": "boolean", + "title": "Completion case-sensitivity", + "description": "Whether or not completions should be offered as case-sensitive.", + "default": false + } + } + } + } +} \ No newline at end of file diff --git a/src/main/resources/messages/LanguageServerBundle.properties b/src/main/resources/messages/LanguageServerBundle.properties index 1e3f52b5d..f837bd7ae 100644 --- a/src/main/resources/messages/LanguageServerBundle.properties +++ b/src/main/resources/messages/LanguageServerBundle.properties @@ -33,10 +33,10 @@ language.server.mappings.fileNamePattern.no=No file name patterns mappings confi language.server.mappings.languageId.column=Language Id language.server.tab.configuration=Configuration +language.server.tab.configuration.server=Server configuration +language.server.tab.configuration.client=Client configuration language.server.configuration=Configuration: -language.server.configuration.emptyText=Defines the configuration of the language server language.server.initializationOptions=Initialization Options: -language.server.initializationOptions.emptyText=Defines the LSP Initialization Options of the language server language.server.tab.debug=Debug language.server.error.reporting=Error reporting: diff --git a/src/main/resources/templates/clojure-lsp/clientSettings.json b/src/main/resources/templates/clojure-lsp/clientSettings.json new file mode 100644 index 000000000..17b22730d --- /dev/null +++ b/src/main/resources/templates/clojure-lsp/clientSettings.json @@ -0,0 +1,5 @@ +{ + "completions": { + "caseSensitive": true + } +} \ No newline at end of file diff --git a/src/main/resources/templates/gopls/clientSettings.json b/src/main/resources/templates/gopls/clientSettings.json new file mode 100644 index 000000000..17b22730d --- /dev/null +++ b/src/main/resources/templates/gopls/clientSettings.json @@ -0,0 +1,5 @@ +{ + "completions": { + "caseSensitive": true + } +} \ No newline at end of file diff --git a/src/main/resources/templates/jdtls/clientSettings.json b/src/main/resources/templates/jdtls/clientSettings.json new file mode 100644 index 000000000..17b22730d --- /dev/null +++ b/src/main/resources/templates/jdtls/clientSettings.json @@ -0,0 +1,5 @@ +{ + "completions": { + "caseSensitive": true + } +} \ No newline at end of file diff --git a/src/main/resources/templates/lemminx/clientSettings.json b/src/main/resources/templates/lemminx/clientSettings.json new file mode 100644 index 000000000..17b22730d --- /dev/null +++ b/src/main/resources/templates/lemminx/clientSettings.json @@ -0,0 +1,5 @@ +{ + "completions": { + "caseSensitive": true + } +} \ No newline at end of file diff --git a/src/main/resources/templates/metals/clientSettings.json b/src/main/resources/templates/metals/clientSettings.json new file mode 100644 index 000000000..17b22730d --- /dev/null +++ b/src/main/resources/templates/metals/clientSettings.json @@ -0,0 +1,5 @@ +{ + "completions": { + "caseSensitive": true + } +} \ No newline at end of file diff --git a/src/main/resources/templates/rust-analyzer/clientSettings.json b/src/main/resources/templates/rust-analyzer/clientSettings.json new file mode 100644 index 000000000..17b22730d --- /dev/null +++ b/src/main/resources/templates/rust-analyzer/clientSettings.json @@ -0,0 +1,5 @@ +{ + "completions": { + "caseSensitive": true + } +} \ No newline at end of file diff --git a/src/main/resources/templates/sourcekit-lsp/clientSettings.json b/src/main/resources/templates/sourcekit-lsp/clientSettings.json new file mode 100644 index 000000000..17b22730d --- /dev/null +++ b/src/main/resources/templates/sourcekit-lsp/clientSettings.json @@ -0,0 +1,5 @@ +{ + "completions": { + "caseSensitive": true + } +} \ No newline at end of file diff --git a/src/main/resources/templates/typescript-language-server/clientSettings.json b/src/main/resources/templates/typescript-language-server/clientSettings.json new file mode 100644 index 000000000..17b22730d --- /dev/null +++ b/src/main/resources/templates/typescript-language-server/clientSettings.json @@ -0,0 +1,5 @@ +{ + "completions": { + "caseSensitive": true + } +} \ No newline at end of file diff --git a/src/main/resources/templates/vscode-css-language-server/clientSettings.json b/src/main/resources/templates/vscode-css-language-server/clientSettings.json new file mode 100644 index 000000000..17b22730d --- /dev/null +++ b/src/main/resources/templates/vscode-css-language-server/clientSettings.json @@ -0,0 +1,5 @@ +{ + "completions": { + "caseSensitive": true + } +} \ No newline at end of file diff --git a/src/main/resources/templates/vscode-html-language-server/clientSettings.json b/src/main/resources/templates/vscode-html-language-server/clientSettings.json new file mode 100644 index 000000000..3cc61256a --- /dev/null +++ b/src/main/resources/templates/vscode-html-language-server/clientSettings.json @@ -0,0 +1,5 @@ +{ + "completions": { + "caseSensitive": false + } +} \ No newline at end of file diff --git a/src/test/java/com/redhat/devtools/lsp4ij/launching/templates/LanguageServerDefinitionSerializerTest.java b/src/test/java/com/redhat/devtools/lsp4ij/launching/templates/LanguageServerDefinitionSerializerTest.java index edb6165a5..806673f19 100644 --- a/src/test/java/com/redhat/devtools/lsp4ij/launching/templates/LanguageServerDefinitionSerializerTest.java +++ b/src/test/java/com/redhat/devtools/lsp4ij/launching/templates/LanguageServerDefinitionSerializerTest.java @@ -38,8 +38,8 @@ public void testBasicUserDefinedLsSerialization() { Map.of(), false, "", - "" - ); + "", + ""); Gson gson = new GsonBuilder() .registerTypeAdapter(UserDefinedLanguageServerDefinition.class, new LanguageServerDefinitionSerializer()) @@ -61,8 +61,8 @@ public void testLanguageMapping() { Map.of(), false, "", - "" - ); + "", + ""); lsDef.getLanguageMappings().put(Language.ANY, "testing"); Gson gson = new GsonBuilder() @@ -88,8 +88,8 @@ public void testFilePatternsMapping() { Map.of(), false, "", - "" - ); + "", + ""); FileNameMatcher fileNameMatcher1 = new ExtensionFileNameMatcher("rs"); FileNameMatcher fileNameMatcher2 = new WildcardFileNameMatcher("*kt"); List fileNameMatcherList = List.of(fileNameMatcher1, fileNameMatcher2); @@ -122,8 +122,8 @@ public void testFileTypeMappings() { Map.of(), false, "", - "" - ); + "", + ""); FileTypeManager fileTypeManager = FileTypeManager.getInstance(); FileType fileType = fileTypeManager.getFileTypeByExtension("any"); lsDef.getFileTypeMappings().put(fileType, "Mock"); diff --git a/src/test/java/com/redhat/devtools/lsp4ij/launching/templates/LanguageServerTemplateManagerTest.java b/src/test/java/com/redhat/devtools/lsp4ij/launching/templates/LanguageServerTemplateManagerTest.java index e4c4abf46..f7866173c 100644 --- a/src/test/java/com/redhat/devtools/lsp4ij/launching/templates/LanguageServerTemplateManagerTest.java +++ b/src/test/java/com/redhat/devtools/lsp4ij/launching/templates/LanguageServerTemplateManagerTest.java @@ -70,6 +70,8 @@ public void testImport() { assertNotEquals("", template.getInitializationOptions()); assertNotNull(template.getConfiguration()); assertNotEquals("", template.getConfiguration()); + assertNotNull(template.getClientConfiguration()); + assertNotEquals("", template.getClientConfiguration()); } @Test @@ -107,7 +109,7 @@ private UserDefinedLanguageServerDefinition createUserDefinedLanguageServerDefin new HashMap<>(), false, null, - null - ); + null, + null); } } \ No newline at end of file