diff --git a/src/main/java/com/redhat/devtools/lsp4ij/LanguageServersRegistry.java b/src/main/java/com/redhat/devtools/lsp4ij/LanguageServersRegistry.java index 55bf7dcf1..e44f13009 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/LanguageServersRegistry.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/LanguageServersRegistry.java @@ -162,6 +162,7 @@ private void loadServersAndMappingFromSettings() { launch.getUserEnvironmentVariables(), launch.isIncludeSystemEnvironmentVariables(), launch.getConfigurationContent(), + launch.getConfigurationSchemaContent(), launch.getInitializationOptionsContent(), launch.getClientConfigurationContent()), mappings); @@ -365,6 +366,7 @@ private void addServerDefinitionWithoutNotification(@NotNull LanguageServerDefin settings.setMappings(toServerMappingSettings(mappings)); } settings.setConfigurationContent(definitionFromSettings.getConfigurationContent()); + settings.setConfigurationSchemaContent(definitionFromSettings.getConfigurationSchemaContent()); settings.setInitializationOptionsContent(definitionFromSettings.getInitializationOptionsContent()); settings.setClientConfigurationContent(definitionFromSettings.getClientConfigurationContent()); UserDefinedLanguageServerSettings.getInstance().setLaunchConfigSettings(languageServerId, settings); @@ -493,6 +495,7 @@ private void removeAssociationsFor(LanguageServerDefinition definition) { settings.setUserEnvironmentVariables(request.userEnvironmentVariables()); settings.setIncludeSystemEnvironmentVariables(request.includeSystemEnvironmentVariables()); settings.setConfigurationContent(request.configurationContent()); + settings.setConfigurationSchemaContent(request.configurationSchemaContent()); settings.setInitializationOptionsContent(request.initializationOptionsContent()); settings.setClientConfigurationContent(request.clientConfigurationContent); settings.setMappings(request.mappings()); @@ -583,6 +586,7 @@ public record UpdateServerDefinitionRequest(@NotNull Project project, boolean includeSystemEnvironmentVariables, @NotNull List mappings, @Nullable String configurationContent, + @Nullable String configurationSchemaContent, @Nullable String initializationOptionsContent, @Nullable String clientConfigurationContent) { } 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 5ecd6ee10..769a433e9 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/launching/UserDefinedLanguageServerSettings.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/launching/UserDefinedLanguageServerSettings.java @@ -117,6 +117,8 @@ public static class UserDefinedLanguageServerItemSettings { private String configurationContent; + private String configurationSchemaContent; + private String initializationOptionsContent; private String clientConfigurationContent = "{}"; @@ -201,6 +203,14 @@ public void setConfigurationContent(String configurationContent) { this.configurationContent = configurationContent; } + public String getConfigurationSchemaContent() { + return configurationSchemaContent; + } + + public void setConfigurationSchemaContent(String configurationSchemaContent) { + this.configurationSchemaContent = configurationSchemaContent; + } + public String getInitializationOptionsContent() { return initializationOptionsContent; } 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 59c7909bb..b14db61e7 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 SETTINGS_SCHEMA_FILE_NAME = "settings.schema.json"; public static final String CLIENT_SETTINGS_FILE_NAME = "clientSettings.json"; public static final String README_FILE_NAME = "README.md"; @@ -70,6 +71,7 @@ public String getName() { private String description; private String configuration; + private String configurationSchema; private String initializationOptions; private String clientConfiguration; @@ -144,6 +146,14 @@ public void setConfiguration(String configuration) { this.configuration = configuration; } + public String getConfigurationSchema() { + return configurationSchema; + } + + public void setConfigurationSchema(String configurationSchema) { + this.configurationSchema = configurationSchema; + } + public String getInitializationOptions() { return initializationOptions; } 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 be32e3807..35c2f3630 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 @@ -134,6 +134,7 @@ public LanguageServerTemplate importLsTemplate(@NotNull VirtualFile templateFold public LanguageServerTemplate createLsTemplate(@NotNull VirtualFile templateFolder) throws IOException { String templateJson = null; String settingsJson = null; + String settingsSchemaJson = null; String initializationOptionsJson = null; String clientSettingsJson = null; String description = null; @@ -149,6 +150,9 @@ public LanguageServerTemplate createLsTemplate(@NotNull VirtualFile templateFold case SETTINGS_FILE_NAME: settingsJson = VfsUtilCore.loadText(file); break; + case SETTINGS_SCHEMA_FILE_NAME: + settingsSchemaJson = VfsUtilCore.loadText(file); + break; case INITIALIZATION_OPTIONS_FILE_NAME: initializationOptionsJson = VfsUtilCore.loadText(file); break; @@ -183,6 +187,7 @@ public LanguageServerTemplate createLsTemplate(@NotNull VirtualFile templateFold LanguageServerTemplate template = gson.fromJson(templateJson, LanguageServerTemplate.class); template.setConfiguration(settingsJson); + template.setConfigurationSchema(settingsSchemaJson); template.setInitializationOptions(initializationOptionsJson); template.setClientConfiguration(clientSettingsJson); if (StringUtils.isNotBlank(description)) { @@ -230,12 +235,14 @@ private SimpleEntry createZipFromLanguageServers(@NotNull List< String template = gson.toJson(lsDefinition); String initializationOptions = ((UserDefinedLanguageServerDefinition) lsDefinition).getInitializationOptionsContent(); String settings = ((UserDefinedLanguageServerDefinition) lsDefinition).getConfigurationContent(); + String settingsSchema = ((UserDefinedLanguageServerDefinition) lsDefinition).getConfigurationSchemaContent(); 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(SETTINGS_SCHEMA_FILE_NAME, settingsSchema, zos); writeToZip(CLIENT_SETTINGS_FILE_NAME, clientSettings, zos); zos.closeEntry(); count++; 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 705b1a62a..09c471d3e 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 @@ -214,6 +214,9 @@ private void loadFromTemplate(LanguageServerTemplate template) { configuration.setText(template.getConfiguration() != null ? template.getConfiguration() : ""); configuration.setCaretPosition(0); + // Update server configuration JSON Schema + this.languageServerPanel.setConfigurationSchemaContent(template.getConfigurationSchema() != null ? template.getConfigurationSchema() : ""); + // Update initialization options var initializationOptions = this.languageServerPanel.getInitializationOptionsWidget(); initializationOptions.setText(template.getInitializationOptions() != null ? template.getInitializationOptions() : ""); @@ -293,6 +296,7 @@ protected void doOKAction() { Map userEnvironmentVariables = this.languageServerPanel.getEnvironmentVariables().getEnvs(); boolean includeSystemEnvironmentVariables = this.languageServerPanel.getEnvironmentVariables().isPassParentEnvs(); String configuration = this.languageServerPanel.getConfiguration().getText(); + String configurationSchema = this.languageServerPanel.getConfigurationSchemaContent(); String initializationOptions = this.languageServerPanel.getInitializationOptionsWidget().getText(); String clientConfiguration = this.languageServerPanel.getClientConfigurationWidget().getText(); UserDefinedLanguageServerDefinition definition = new UserDefinedLanguageServerDefinition(serverId, @@ -302,6 +306,7 @@ protected void doOKAction() { userEnvironmentVariables, includeSystemEnvironmentVariables, configuration, + configurationSchema, initializationOptions, clientConfiguration); LanguageServersRegistry.getInstance().addServerDefinition(project, definition, mappingSettings); @@ -315,4 +320,10 @@ protected void textChanged(@NotNull DocumentEvent e) { } }); } + + @Override + protected void dispose() { + this.languageServerPanel.dispose(); + super.dispose(); + } } 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 f683f04d4..0816fa514 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 @@ -45,6 +45,7 @@ public class UserDefinedLanguageServerDefinition extends LanguageServerDefinitio private boolean includeSystemEnvironmentVariables; private String configurationContent; private Object configuration; + private String configurationSchemaContent; private String initializationOptionsContent; private Object initializationOptions; private String clientConfigurationContent; @@ -57,6 +58,7 @@ public UserDefinedLanguageServerDefinition(@NotNull String id, @NotNull Map userEnvironmentVariables, boolean includeSystemEnvironmentVariables, @Nullable String configurationContent, + @Nullable String configurationSchemaContent, @Nullable String initializationOptionsContent, @Nullable String clientConfigurationContent) { super(id, name, description, true, null, false); @@ -65,6 +67,7 @@ public UserDefinedLanguageServerDefinition(@NotNull String id, this.userEnvironmentVariables = userEnvironmentVariables; this.includeSystemEnvironmentVariables = includeSystemEnvironmentVariables; this.configurationContent = configurationContent; + this.configurationSchemaContent = configurationSchemaContent; this.initializationOptionsContent = initializationOptionsContent; this.clientConfigurationContent = clientConfigurationContent; } @@ -85,6 +88,7 @@ public UserDefinedLanguageServerDefinition(@NotNull String id, userEnvironmentVariables, includeSystemEnvironmentVariables, configurationContent, + null, initializationOptionsContent, null); } @@ -157,6 +161,14 @@ public void setConfigurationContent(String configurationContent) { this.configuration = null; } + public String getConfigurationSchemaContent() { + return configurationSchemaContent; + } + + public void setConfigurationSchemaContent(String configurationSchemaContent) { + this.configurationSchemaContent = configurationSchemaContent; + } + public String getInitializationOptionsContent() { return initializationOptionsContent; } 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 af872a8db..33750db55 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/settings/LanguageServerView.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/settings/LanguageServerView.java @@ -103,6 +103,7 @@ && isEquals(this.getCommandLine(), settings.getCommandLine()) && this.getEnvData().isPassParentEnvs() == settings.isIncludeSystemEnvironmentVariables() && Objects.equals(this.getMappings(), settings.getMappings()) && isEquals(this.getConfigurationContent(), settings.getConfigurationContent()) + && isEquals(this.getConfigurationSchemaContent(), settings.getConfigurationSchemaContent()) && isEquals(this.getInitializationOptionsContent(), settings.getInitializationOptionsContent()) && isEquals(this.getClientConfigurationContent(), settings.getClientConfigurationContent()))) { return true; @@ -171,6 +172,7 @@ public void reset() { userDefinedLanguageServerSettings.getUserEnvironmentVariables(), userDefinedLanguageServerSettings.isIncludeSystemEnvironmentVariables())); this.setConfigurationContent(userDefinedLanguageServerSettings.getConfigurationContent()); + this.setConfigurationSchemaContent(userDefinedLanguageServerSettings.getConfigurationSchemaContent()); this.setInitializationOptionsContent(userDefinedLanguageServerSettings.getInitializationOptionsContent()); this.setClientConfigurationContent(userDefinedLanguageServerSettings.getClientConfigurationContent()); @@ -256,7 +258,18 @@ 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(), getClientConfigurationContent()), false); + new LanguageServersRegistry.UpdateServerDefinitionRequest(project, + launch, + getDisplayName(), + getCommandLine(), + getEnvData().getEnvs(), + getEnvData().isPassParentEnvs(), + getMappings(), + getConfigurationContent(), + getConfigurationSchemaContent(), + getInitializationOptionsContent(), + getClientConfigurationContent()), + false); if (settingsChangedEvent != null) { // Settings has changed, fire the event com.redhat.devtools.lsp4ij.settings.UserDefinedLanguageServerSettings @@ -400,6 +413,14 @@ public void setConfigurationContent(String configurationContent) { configuration.setCaretPosition(0); } + public String getConfigurationSchemaContent() { + return languageServerPanel.getConfigurationSchemaContent(); + } + + public void setConfigurationSchemaContent(String configurationSchemaContent) { + languageServerPanel.setConfigurationSchemaContent(configurationSchemaContent); + } + public String getInitializationOptionsContent() { return languageServerPanel.getInitializationOptionsWidget().getText(); } @@ -422,6 +443,7 @@ public void setClientConfigurationContent(String configurationContent) { @Override public void dispose() { + languageServerPanel.dispose(); } public List getMappings() { 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 index 131f4e2fd..b735468f8 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/settings/jsonSchema/AbstractLSPJsonSchemaFileProvider.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/settings/jsonSchema/AbstractLSPJsonSchemaFileProvider.java @@ -13,46 +13,30 @@ *******************************************************************************/ package com.redhat.devtools.lsp4ij.settings.jsonSchema; +import com.intellij.openapi.application.ModalityState; +import com.intellij.openapi.application.WriteAction; +import com.intellij.openapi.project.Project; 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.intellij.psi.impl.PsiManagerEx; +import com.intellij.psi.impl.file.impl.FileManagerImpl; +import com.intellij.util.ModalityUiUtil; 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; + protected AbstractLSPJsonSchemaFileProvider(@NotNull String jsonFilename) { 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()); @@ -80,4 +64,19 @@ public final JsonSchemaVersion getSchemaVersion() { public final String getPresentableName() { return getName(); } + + @Override + public boolean isUserVisible() { + return false; + } + + protected static void reloadPsi(@NotNull VirtualFile file, + @NotNull Project project) { + final FileManagerImpl fileManager = (FileManagerImpl) PsiManagerEx.getInstanceEx(project).getFileManager(); + if (fileManager.findCachedViewProvider(file) != null) { + ModalityUiUtil.invokeLaterIfNeeded(ModalityState.defaultModalityState(), project.getDisposed(), + () -> WriteAction.run(() -> fileManager.forceReload(file)) + ); + } + } } diff --git a/src/main/java/com/redhat/devtools/lsp4ij/settings/jsonSchema/AbstractLSPJsonSchemaFileSystemProvider.java b/src/main/java/com/redhat/devtools/lsp4ij/settings/jsonSchema/AbstractLSPJsonSchemaFileSystemProvider.java new file mode 100644 index 000000000..3fc793a6f --- /dev/null +++ b/src/main/java/com/redhat/devtools/lsp4ij/settings/jsonSchema/AbstractLSPJsonSchemaFileSystemProvider.java @@ -0,0 +1,51 @@ +/******************************************************************************* + * 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.vfs.VfsUtil; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.openapi.vfs.VirtualFileManager; +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 AbstractLSPJsonSchemaFileSystemProvider extends AbstractLSPJsonSchemaFileProvider { + private final String jsonSchemaPath; + private VirtualFile jsonSchemaFile = null; + + protected AbstractLSPJsonSchemaFileSystemProvider(@NotNull String jsonSchemaPath, @NotNull String jsonFilename) { + super(jsonFilename); + this.jsonSchemaPath = jsonSchemaPath; + } + + @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; + } + +} 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 index e4b300b55..e52be5329 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/settings/jsonSchema/LSPClientConfigurationJsonSchemaFileProvider.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/settings/jsonSchema/LSPClientConfigurationJsonSchemaFileProvider.java @@ -16,7 +16,7 @@ /** * JSON schema file provider for language server client-side configuration. */ -public class LSPClientConfigurationJsonSchemaFileProvider extends AbstractLSPJsonSchemaFileProvider { +public class LSPClientConfigurationJsonSchemaFileProvider extends AbstractLSPJsonSchemaFileSystemProvider { private static final String CLIENT_SETTINGS_SCHEMA_JSON_PATH = "/jsonSchema/clientSettings.schema.json"; public static final String CLIENT_SETTINGS_JSON_FILE_NAME = "clientSettings.json"; diff --git a/src/main/java/com/redhat/devtools/lsp4ij/settings/jsonSchema/LSPJsonSchemaLightVirtualFile.java b/src/main/java/com/redhat/devtools/lsp4ij/settings/jsonSchema/LSPJsonSchemaLightVirtualFile.java new file mode 100644 index 000000000..3dd67a870 --- /dev/null +++ b/src/main/java/com/redhat/devtools/lsp4ij/settings/jsonSchema/LSPJsonSchemaLightVirtualFile.java @@ -0,0 +1,145 @@ +// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.redhat.devtools.lsp4ij.settings.jsonSchema; + +import com.intellij.lang.Language; +import com.intellij.openapi.fileTypes.CharsetUtil; +import com.intellij.openapi.fileTypes.FileType; +import com.intellij.openapi.fileTypes.FileTypeRegistry; +import com.intellij.openapi.util.NlsSafe; +import com.intellij.openapi.vfs.VfsUtilCore; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.testFramework.LightVirtualFileBase; +import com.intellij.util.ArrayUtil; +import com.intellij.util.LocalTimeCounter; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.*; +import java.nio.charset.Charset; + +/** + * In-memory implementation of {@link VirtualFile}. + * + *

We cannot use {@link com.intellij.testFramework.LightVirtualFile} + * because IJ 2024.x? doesn't evict the Json Schema cache when {@link com.intellij.testFramework.LightVirtualFile} is used + * because it skips the modificationStamp (when file is updated). + * + * See JsonSchemaObjectStorage + * + *

+ * + *

+ * This class is a copy/paste of LightVirtualFile.java + *

+ */ +class LSPJsonSchemaLightVirtualFile extends LightVirtualFileBase { + private CharSequence myContent; + private Language myLanguage; + private long myCachedLength = Long.MIN_VALUE; + + public LSPJsonSchemaLightVirtualFile(@NlsSafe @NotNull String name, @NotNull CharSequence content) { + this(name, null, content, LocalTimeCounter.currentTime()); + } + + public LSPJsonSchemaLightVirtualFile(@NlsSafe @NotNull String name, @Nullable FileType fileType, @NotNull CharSequence text, long modificationStamp) { + this(name, fileType, text, CharsetUtil.extractCharsetFromFileContent(null, null, fileType, text), modificationStamp); + } + + public LSPJsonSchemaLightVirtualFile(@NlsSafe @NotNull String name, + @Nullable FileType fileType, + @NlsSafe @NotNull CharSequence text, + Charset charset, + long modificationStamp) { + super(name, fileType, modificationStamp); + setContentImpl(text); + setCharset(charset); + } + + @Override + protected void storeCharset(Charset charset) { + super.storeCharset(charset); + myCachedLength = Long.MIN_VALUE; + } + + public Language getLanguage() { + return myLanguage; + } + + public void setLanguage(@NotNull Language language) { + myLanguage = language; + FileType type = language.getAssociatedFileType(); + if (type == null) { + type = FileTypeRegistry.getInstance().getFileTypeByFileName(getNameSequence()); + } + setFileType(type); + } + + @Override + public @NotNull InputStream getInputStream() throws IOException { + return VfsUtilCore.byteStreamSkippingBOM(doGetContent(), this); + } + + @Override + public long getLength() { + long cachedLength = myCachedLength; + if (cachedLength == Long.MIN_VALUE) { + myCachedLength = cachedLength = super.getLength(); + } + return cachedLength; + } + + @Override + public @NotNull OutputStream getOutputStream(Object requestor, final long newModificationStamp, long newTimeStamp) throws IOException { + return VfsUtilCore.outputStreamAddingBOM(new ByteArrayOutputStream() { + @Override + public void close() { + assert isWritable(); + + setModificationStamp(newModificationStamp); + try { + setContentImpl(toString(getCharset().name())); + } + catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + } + }, this); + } + + @Override + public byte @NotNull [] contentsToByteArray() throws IOException { + //long cachedLength = myCachedLength; + //if (FileSizeLimit.isTooLarge(cachedLength, FileUtilRt.getExtension(getNameSequence()).toString())) { + // throw new FileTooBigException("file too big, length = "+cachedLength); + //} + return doGetContent(); + } + + private byte @NotNull [] doGetContent() { + Charset charset = getCharset(); + String s = getContent().toString(); + byte[] result = s.getBytes(charset); + byte[] bom = getBOM(); + return bom == null ? result : ArrayUtil.mergeArrays(bom, result); + } + + public void setContent(@NotNull CharSequence content) { + setContentImpl(content); + setModificationStamp(LocalTimeCounter.currentTime()); + } + + private void setContentImpl(@NotNull CharSequence content) { + myContent = content; + myCachedLength = Long.MIN_VALUE; + } + + public @NotNull CharSequence getContent() { + return myContent; + } + + @Override + public String toString() { + return "MyLightVirtualFile: " + getPresentableUrl(); + } + +} 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 index ce32cc04b..75eccf806 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/settings/jsonSchema/LSPJsonSchemaProviderFactory.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/settings/jsonSchema/LSPJsonSchemaProviderFactory.java @@ -18,17 +18,21 @@ import com.jetbrains.jsonSchema.extension.JsonSchemaProviderFactory; import org.jetbrains.annotations.NotNull; +import java.util.ArrayList; 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() - ); + List providers = new ArrayList<>(); + providers.add(new LSPClientConfigurationJsonSchemaFileProvider()); + // Create 100 dummy JsonSchemaFileProvider used by Server / Configuration editors. + providers.addAll(LSPServerConfigurationJsonSchemaManager.getInstance(project).getProviders()); + return providers; } } diff --git a/src/main/java/com/redhat/devtools/lsp4ij/settings/jsonSchema/LSPServerConfigurationJsonSchemaFileProvider.java b/src/main/java/com/redhat/devtools/lsp4ij/settings/jsonSchema/LSPServerConfigurationJsonSchemaFileProvider.java new file mode 100644 index 000000000..553351ca7 --- /dev/null +++ b/src/main/java/com/redhat/devtools/lsp4ij/settings/jsonSchema/LSPServerConfigurationJsonSchemaFileProvider.java @@ -0,0 +1,116 @@ +/******************************************************************************* + * 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.intellij.openapi.vfs.VirtualFile; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Objects; + +/** + * Json Schema provider used in the Server / Configuration editor. + */ +public class LSPServerConfigurationJsonSchemaFileProvider extends AbstractLSPJsonSchemaFileProvider { + + private final int index; + private final @NotNull Project project; + private final @NotNull LSPJsonSchemaLightVirtualFile file; + private boolean unused; + + public LSPServerConfigurationJsonSchemaFileProvider(int index, @NotNull Project project) { + super(generateJsonFileName(index)); + this.index = index; + this.project = project; + this.file = new LSPJsonSchemaLightVirtualFile(generateJsonSchemaFileName(index), ""); + this.unused = true; + } + + private static String generateJsonFileName(int index) { + return "lsp.server." + index + ".json"; + } + + private static String generateJsonSchemaFileName(int index) { + return "lsp.server." + index + ".schema.json"; + } + + /** + * Returns the index of the provider stored in the provider pool. + * + * @return the index of the provider stored in the provider pool. + */ + public int getIndex() { + return index; + } + + @Override + public @Nullable VirtualFile getSchemaFile() { + return file; + } + + /** + * Free the Json Schema provider which can be used by a Server / Configuration editor. + */ + public void reset() { + try { + updateFileContent("", file, project); + } finally { + unused = true; + } + } + + /** + * Update the file with the given json schema content. + * + * @param jsonSchemaContent the Json schema content. + */ + public void setSchemaContent(@NotNull String jsonSchemaContent) { + try { + updateFileContent(jsonSchemaContent, file, project); + } finally { + unused = false; + } + } + + /** + * Returns true if the Json Schema provider is unused (by a Server / Configuration editor) and false otherwise. + * + * @return true if the Json Schema provider is unused (by a Server / Configuration editor) and false otherwise. + */ + public boolean isUnused() { + return unused; + } + + /** + * Update file content. + * + * @param content the new content. + * @param file the file to update. + * @param project the project. + */ + private static void updateFileContent(@NotNull String content, + @NotNull LSPJsonSchemaLightVirtualFile file, + @NotNull Project project) { + if (Objects.equals(content, file.getContent())) { + // No changes, don't update the file. + return; + } + // Update the virtual file content and the modification stamp (used by Json Schema cache) + file.setContent(content); + // Synchronize the Psi file from the new content of the virtual file and the modification stamp (used by Json Schema cache) + reloadPsi(file, project); + } + +} diff --git a/src/main/java/com/redhat/devtools/lsp4ij/settings/jsonSchema/LSPServerConfigurationJsonSchemaManager.java b/src/main/java/com/redhat/devtools/lsp4ij/settings/jsonSchema/LSPServerConfigurationJsonSchemaManager.java new file mode 100644 index 000000000..541005525 --- /dev/null +++ b/src/main/java/com/redhat/devtools/lsp4ij/settings/jsonSchema/LSPServerConfigurationJsonSchemaManager.java @@ -0,0 +1,91 @@ +/******************************************************************************* + * 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 org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; + +/** + * Pooling of JsonSchemaProvider used by Server / Configuration editors. + * We need this pooling because there are no way to register {@link com.jetbrains.jsonSchema.extension.JsonSchemaFileProvider} + * dynamically with {@link LSPJsonSchemaProviderFactory}. + */ +public class LSPServerConfigurationJsonSchemaManager { + + private static final int JSON_SCHEMA_FILE_PROVIDER_POOL_SIZE = 100; + + public static LSPServerConfigurationJsonSchemaManager getInstance(@NotNull Project project) { + return project.getService(LSPServerConfigurationJsonSchemaManager.class); + } + + private final List providers; + + public LSPServerConfigurationJsonSchemaManager(@NotNull Project project) { + providers = new ArrayList<>(); + // Create 100 dummy LSPServerConfigurationJsonSchemaFileProvider + for (int i = 0; i < JSON_SCHEMA_FILE_PROVIDER_POOL_SIZE; i++) { + providers.add(new LSPServerConfigurationJsonSchemaFileProvider(i, project)); + } + } + + public List getProviders() { + return providers; + } + + /** + * Returns the first index of a {@link LSPServerConfigurationJsonSchemaFileProvider} which is not used by + * a Server / Configuration editor and null otherwise. + * + * @return the first index of a {@link LSPServerConfigurationJsonSchemaFileProvider} which is not used by + * * a Server / Configuration editor and null otherwise. + */ + @Nullable + public Integer getUnusedIndex() { + var result = getProviders() + .stream() + .filter(LSPServerConfigurationJsonSchemaFileProvider::isUnused) + .map(LSPServerConfigurationJsonSchemaFileProvider::getIndex) + .findFirst(); + return result.isPresent() ? result.get() : null; + } + + /** + * Update the Json schema content of the {@link LSPServerConfigurationJsonSchemaFileProvider} stored in the given index. + * + * @param index the index of {@link LSPServerConfigurationJsonSchemaFileProvider} instance to update. + * @param jsonSchemaContent the new Json schema content. + * @return the file name to use to associate with the {@link LSPServerConfigurationJsonSchemaFileProvider} instance updated. + */ + public String setJsonSchemaContent(@NotNull Integer index, + @Nullable String jsonSchemaContent) { + LSPServerConfigurationJsonSchemaFileProvider provider = getProviders().get(index); + provider.setSchemaContent(jsonSchemaContent); + return provider.getName(); + } + + /** + * Free the {@link LSPServerConfigurationJsonSchemaFileProvider} stored at the given index from the pool + * (when a Server / Configuration editor is disposed). + * + * @param index the index of {@link LSPServerConfigurationJsonSchemaFileProvider}. + */ + public void reset(@NotNull Integer index) { + LSPServerConfigurationJsonSchemaFileProvider provider = getProviders().get(index); + provider.reset(); + } +} diff --git a/src/main/java/com/redhat/devtools/lsp4ij/settings/ui/EditJsonSchemaDialog.java b/src/main/java/com/redhat/devtools/lsp4ij/settings/ui/EditJsonSchemaDialog.java new file mode 100644 index 000000000..c4d9e9eb4 --- /dev/null +++ b/src/main/java/com/redhat/devtools/lsp4ij/settings/ui/EditJsonSchemaDialog.java @@ -0,0 +1,73 @@ +/******************************************************************************* + * 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 com.intellij.openapi.ui.DialogWrapper; +import com.intellij.util.ui.FormBuilder; +import com.redhat.devtools.lsp4ij.LanguageServerBundle; +import com.redhat.devtools.lsp4ij.internal.StringUtils; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import java.awt.*; + +/** + * Dialog to edit Json Schema. + */ +public class EditJsonSchemaDialog extends DialogWrapper { + + + private final @NotNull Project project; + private String jsonSchemaContent; + private JsonTextField jsonSchemaWidget; + + protected EditJsonSchemaDialog(@NotNull Project project, String jsonSchemaContent) { + super(true); + this.project = project; + this.jsonSchemaContent = jsonSchemaContent; + super.setTitle(LanguageServerBundle.message("edit.json.schema.dialog.title")); + init(); + } + + @Override + protected @Nullable JComponent createCenterPanel() { + FormBuilder builder = FormBuilder + .createFormBuilder(); + + jsonSchemaWidget = new JsonTextField(project); + builder.addLabeledComponentFillVertically(LanguageServerBundle.message("edit.json.schema.dialog.schema"), jsonSchemaWidget); + if (StringUtils.isNotBlank(jsonSchemaContent)) { + jsonSchemaWidget.setText(jsonSchemaContent); + } + + JPanel panel = new JPanel(new BorderLayout()); + panel.add(builder.getPanel(), BorderLayout.CENTER); + return panel; + } + + @Override + public void show() { + // How to set the focus in the editor to see the cursor when dialog opens? + jsonSchemaWidget.setCaretPosition(0); + super.show(); + } + + /** + * Returns the Json Schema content. + * + * @return the Json Schema content. + */ + public String getJsonSchemaContent() { + return jsonSchemaWidget.getText(); + } +} 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 27b5d65ae..72a558642 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 @@ -20,6 +20,7 @@ import com.intellij.openapi.vfs.VirtualFile; import com.intellij.ui.*; import com.intellij.util.containers.ContainerUtil; +import com.redhat.devtools.lsp4ij.settings.jsonSchema.LSPServerConfigurationJsonSchemaManager; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; @@ -42,9 +43,13 @@ public class JsonTextField extends JPanel { private static final String JSON_LANGUAGE_NAME = "JSON"; private static final String DEFAULT_VALUE = "{}"; + private final @NotNull Project project; + private final EditorTextField editorTextField; + private @Nullable Integer index; public JsonTextField(@NotNull Project project) { + this.project = project; // Create and initialize the editor text field EditorTextFieldProvider service = ApplicationManager.getApplication().getService(EditorTextFieldProvider.class); List features = new ArrayList<>(); @@ -85,6 +90,16 @@ public void setJsonFilename(@NotNull String jsonFilename) { } } + public void associateWithJsonSchema(@NotNull String jsonSchemaContent) { + var manager = LSPServerConfigurationJsonSchemaManager.getInstance(project); + if (index == null) { + index = manager.getUnusedIndex(); + } + if (index != null) { + String jsonFileName = manager.setJsonSchemaContent(index, jsonSchemaContent); + setJsonFilename(jsonFileName); + } + } // Proxy some simple accessors to the editor text field public void setText(@Nullable String text) { @@ -98,4 +113,12 @@ public void setText(@Nullable String text) { public void setCaretPosition(int position) { editorTextField.setCaretPosition(position); } + + public void resetJsonSchema() { + setJsonFilename("lsp.server.settings.no.schema.json"); + if (index != null) { + var manager = LSPServerConfigurationJsonSchemaManager.getInstance(project); + manager.reset(index); + } + } } 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 66c03676e..902ec29e9 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 @@ -12,10 +12,12 @@ import com.intellij.execution.configuration.EnvironmentVariablesComponent; import com.intellij.ide.BrowserUtil; +import com.intellij.openapi.Disposable; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.ComboBox; import com.intellij.openapi.util.NlsContexts; import com.intellij.ui.ContextHelpLabel; +import com.intellij.ui.HyperlinkLabel; import com.intellij.ui.PortField; import com.intellij.ui.SimpleListCellRenderer; import com.intellij.ui.components.JBCheckBox; @@ -27,6 +29,7 @@ import com.intellij.util.ui.UIUtil; import com.intellij.util.ui.components.BorderLayoutPanel; import com.redhat.devtools.lsp4ij.LanguageServerBundle; +import com.redhat.devtools.lsp4ij.internal.StringUtils; import com.redhat.devtools.lsp4ij.server.definition.launching.UserDefinedLanguageServerDefinition; import com.redhat.devtools.lsp4ij.settings.ErrorReportingKind; import com.redhat.devtools.lsp4ij.settings.ServerTrace; @@ -44,15 +47,16 @@ *
    *
  • Server tab
  • *
  • Mappings tab
  • - *
  • Configuration tab
  • + *
  • Configuration tab which hosts Server / Client configuration tabs
  • *
  • Debug tab
  • *
*/ -public class LanguageServerPanel { +public class LanguageServerPanel implements Disposable { private static final int COMMAND_LENGTH_MAX = 140; private final Project project; + private HyperlinkLabel editJsonSchemaAction; public enum EditionMode { NEW_USER_DEFINED, @@ -71,6 +75,7 @@ public enum EditionMode { private final JBCheckBox debugSuspendCheckBox = new JBCheckBox(LanguageServerBundle.message("language.server.debug.suspend")); private JsonTextField configurationWidget; + private String configurationSchemaContent; private JsonTextField initializationOptionsWidget; private JsonTextField clientConfigurationWidget; @@ -261,7 +266,16 @@ private static JLabel createLabelForComponent(@NotNull @NlsContexts.Label String private void createConfigurationField(FormBuilder builder) { configurationWidget = new JsonTextField(project); - builder.addLabeledComponentFillVertically(LanguageServerBundle.message("language.server.configuration"), configurationWidget); + editJsonSchemaAction = new HyperlinkLabel(LanguageServerBundle.message("language.server.configuration.json.schema.associate")); + builder.addLabeledComponent(LanguageServerBundle.message("language.server.configuration"), editJsonSchemaAction); + builder.addComponentFillVertically(configurationWidget, 0); + editJsonSchemaAction.addHyperlinkListener(e-> { + var dialog = new EditJsonSchemaDialog(project, configurationSchemaContent); + dialog.show(); + if (dialog.isOK()) { + this.setConfigurationSchemaContent(dialog.getJsonSchemaContent()); + } + }); } private void createInitializationOptionsTabField(FormBuilder builder) { @@ -296,6 +310,21 @@ public JsonTextField getConfiguration() { return configurationWidget; } + public String getConfigurationSchemaContent() { + return configurationSchemaContent; + } + + public void setConfigurationSchemaContent(String configurationSchemaContent) { + this.configurationSchemaContent = configurationSchemaContent; + if (StringUtils.isNotBlank(configurationSchemaContent)) { + getConfiguration().associateWithJsonSchema(configurationSchemaContent); + editJsonSchemaAction.setHyperlinkText(LanguageServerBundle.message("language.server.configuration.json.schema.edit")); + } else { + getConfiguration().resetJsonSchema(); + editJsonSchemaAction.setHyperlinkText(LanguageServerBundle.message("language.server.configuration.json.schema.associate")); + } + } + public JsonTextField getInitializationOptionsWidget() { return initializationOptionsWidget; } @@ -319,4 +348,9 @@ public ComboBox getServerTraceComboBox() { public ComboBox getErrorReportingKindCombo() { return errorReportingKindCombo; } + + @Override + public void dispose() { + getConfiguration().resetJsonSchema(); + } } diff --git a/src/main/resources/META-INF/plugin-json.xml b/src/main/resources/META-INF/plugin-json.xml index c6e15a97f..7fa4d41e5 100644 --- a/src/main/resources/META-INF/plugin-json.xml +++ b/src/main/resources/META-INF/plugin-json.xml @@ -1,5 +1,9 @@ + + + diff --git a/src/main/resources/messages/LanguageServerBundle.properties b/src/main/resources/messages/LanguageServerBundle.properties index 8e28bc87a..de225bd08 100644 --- a/src/main/resources/messages/LanguageServerBundle.properties +++ b/src/main/resources/messages/LanguageServerBundle.properties @@ -37,6 +37,8 @@ language.server.tab.configuration.server=Server language.server.tab.configuration.client=Client language.server.configuration=Configuration: language.server.initializationOptions=Initialization Options: +language.server.configuration.json.schema.associate=Associate with JSON Schema +language.server.configuration.json.schema.edit=Edit JSON Schema language.server.tab.debug=Debug language.server.error.reporting=Error reporting: @@ -71,6 +73,10 @@ new.language.server.dialog.import.template.selection=Import from custom template new.language.server.dialog.validation.serverName.must.be.set=Server name must be set new.language.server.dialog.validation.commandLine.must.be.set=Command must be set +## Edit Json Schema dialog +edit.json.schema.dialog.title=Edit Json Schema +edit.json.schema.dialog.schema=Json Schema + ## LSP console lsp.console.title=LSP Consoles lsp.console.tabs.traces.title=Traces diff --git a/src/main/resources/templates/typescript-language-server/settings.schema.json b/src/main/resources/templates/typescript-language-server/settings.schema.json new file mode 100644 index 000000000..b4a0ffd06 --- /dev/null +++ b/src/main/resources/templates/typescript-language-server/settings.schema.json @@ -0,0 +1,554 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "LSP4IJ/typescript-language-server/settings.schema.json", + "title": "LSP4IJ TypeScript language server settings JSON schema", + "description": "JSON schema for TypeScript language server settings.", + "type": "object", + "additionalProperties": false, + "properties": { + "completions": { + "type": "object", + "title": "Completion settings", + "properties": { + "completeFunctionCalls": { + "type": "boolean", + "default": false, + "description": "Complete functions with their parameter signature." + } + } + }, + "javascript": { + "$ref": "#/definitions/languageServerConfiguration" + }, + "typescript": { + "$ref": "#/definitions/languageServerConfiguration" + }, + "javascriptreact": { + "$ref": "#/definitions/languageServerConfiguration" + }, + "typescriptreact": { + "$ref": "#/definitions/languageServerConfiguration" + } + }, + "definitions": { + "languageServerConfiguration": { + "type": "object", + "title": "Language server configuration", + "properties": { + "format": { + "type": "object", + "title": "TypeScript code formatter settings", + "properties": { + "baseIndentSize": { + "type": "number", + "title": "Base indent size", + "default": 0 + }, + "convertTabsToSpaces": { + "type": "boolean", + "title": "Convert tabs to spaces", + "default": true + }, + "indentSize": { + "type": "number", + "title": "Indent size", + "default": 4 + }, + "indentStyle": { + "type": "string", + "enum": [ + "None", + "Block", + "Smart" + ], + "title": "Indent style", + "default": "Smart" + }, + "indentSwitchCase": { + "type": "boolean", + "title": "Indent switch case", + "default": true + }, + "insertSpaceAfterCommaDelimiter": { + "type": "boolean", + "title": "Insert space after comma delimiter", + "default": true + }, + "insertSpaceAfterConstructor": { + "type": "boolean", + "title": "Insert space after constructor", + "default": false + }, + "insertSpaceAfterFunctionKeywordForAnonymousFunctions": { + "type": "boolean", + "title": "Insert space after function keyword for anonymous functions", + "default": false + }, + "insertSpaceAfterKeywordsInControlFlowStatements": { + "type": "boolean", + "title": "Insert space after keywords in control flow statements", + "default": true + }, + "insertSpaceAfterOpeningAndBeforeClosingEmptyBraces": { + "type": "boolean", + "title": "Insert space after opening and before closing empty braces", + "default": false + }, + "insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces": { + "type": "boolean", + "title": "Insert space after opening and before closing JSX expression braces", + "default": false + }, + "insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces": { + "type": "boolean", + "title": "Insert space after opening and before closing non-empty braces", + "default": true + }, + "insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets": { + "type": "boolean", + "title": "Insert space after opening and before closing non-empty brackets", + "default": false + }, + "insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis": { + "type": "boolean", + "title": "Insert space after opening and before closing non-empty parenthesis", + "default": false + }, + "insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces": { + "type": "boolean", + "title": "Insert space after opening and before closing template string braces", + "default": false + }, + "insertSpaceAfterSemicolonInForStatements": { + "type": "boolean", + "title": "Insert space after semicolon in for statements", + "default": true + }, + "insertSpaceAfterTypeAssertion": { + "type": "boolean", + "title": "Insert space after type assertion", + "default": false + }, + "insertSpaceBeforeAndAfterBinaryOperators": { + "type": "boolean", + "title": "Insert space before and after binary operators", + "default": true + }, + "insertSpaceBeforeFunctionParenthesis": { + "type": "boolean", + "title": "Insert space before function parenthesis", + "default": false + }, + "insertSpaceBeforeTypeAnnotation": { + "type": "boolean", + "title": "Insert space before type annotation", + "default": true + }, + "newLineCharacter": { + "type": "string", + "title": "Newline character" + }, + "placeOpenBraceOnNewLineForControlBlocks": { + "type": "boolean", + "title": "Place open brace on new line for control blocks", + "default": false + }, + "placeOpenBraceOnNewLineForFunctions": { + "type": "boolean", + "title": "Place open brace on new line for functions", + "default": false + }, + "semicolons": { + "type": "string", + "enum": [ + "ignore", + "insert", + "remove" + ], + "title": "Semicolon policy", + "default": "ignore" + }, + "tabSize": { + "type": "number", + "title": "Tab size", + "default": 4 + }, + "trimTrailingWhitespace": { + "type": "boolean", + "title": "Trim trailing whitespace", + "default": true + } + } + }, + "implementationCodeLens": { + "type": "object", + "title": "TypeScript implementation code lens settings", + "properties": { + "enabled": { + "type": "boolean", + "title": "Whether or not implementation code lens is enabled", + "default": true + } + } + }, + "referencesCodeLens": { + "type": "object", + "title": "TypeScript references code lens settings", + "properties": { + "enabled": { + "type": "boolean", + "title": "Whether or not references code lens is enabled", + "default": true + }, + "showOnAllFunctions": { + "type": "boolean", + "title": "Whether or not references code lens should be displayed for all functions", + "default": true + } + } + }, + "inlayHints": { + "type": "object", + "title": "TypeScript inlay hints settings", + "properties": { + "includeInlayEnumMemberValueHints": { + "type": "boolean", + "title": "Include inlay enum member value hints", + "default": false + }, + "includeInlayFunctionLikeReturnTypeHints": { + "type": "boolean", + "title": "Include inlay function-like return type hints", + "default": false + }, + "includeInlayFunctionParameterTypeHints": { + "type": "boolean", + "title": "Include inlay function parameter type hints", + "default": false + }, + "includeInlayParameterNameHints": { + "type": "string", + "enum": [ + "none", + "literals", + "all" + ], + "title": "Include inlay parameter name hints", + "default": "none" + }, + "includeInlayParameterNameHintsWhenArgumentMatchesName": { + "type": "boolean", + "title": "Include inlay parameter name hints when argument matches name", + "default": false + }, + "includeInlayPropertyDeclarationTypeHints": { + "type": "boolean", + "title": "Include inlay property declaration type hints", + "default": false + }, + "includeInlayVariableTypeHints": { + "type": "boolean", + "title": "Include inlay variable type hints", + "default": false + }, + "includeInlayVariableTypeHintsWhenTypeMatchesName": { + "type": "boolean", + "title": "Include inlay variable type hints when type matches name", + "default": false + } + } + }, + "preferences": { + "type": "object", + "title": "Language server preferences", + "properties": { + "allowIncompleteCompletions": { + "type": "boolean", + "title": "Allow incomplete completions", + "description": "Allows import module names to be resolved in the initial completions request.", + "default": true + }, + "allowRenameOfImportPath": { + "type": "boolean", + "title": "Allow rename of import path", + "default": true + }, + "allowTextChangesInNewFiles": { + "type": "boolean", + "title": "Allow text changes in new files", + "default": true + }, + "autoImportFileExcludePatterns": { + "type": "array", + "items": { + "type": "string" + }, + "title": "Auto-import file exclude patterns", + "description": "Glob patterns of files to exclude from auto imports. Relative paths are resolved relative to the workspace root.", + "default": "shortest" + }, + "disableSuggestions": { + "type": "boolean", + "title": "Disable suggestions", + "default": false + }, + "displayPartsForJSDoc": { + "type": "boolean", + "title": "Display parts for JSDoc", + "default": true + }, + "excludeLibrarySymbolsInNavTo": { + "type": "boolean", + "title": "Exclude library symbols in nav to", + "default": true + }, + "generateReturnInDocTemplate": { + "type": "boolean", + "title": "Generate return in doc template", + "default": true + }, + "importModuleSpecifier": { + "type": "string", + "enum": [ + "shortest", + "project-relative", + "relative", + "non-relative" + ], + "title": "Import module specifier", + "default": "shortest" + }, + "importModuleSpecifierPreference": { + "type": "string", + "enum": [ + "shortest", + "project-relative", + "relative", + "non-relative" + ], + "title": "Import module specifier preference", + "default": "shortest" + }, + "importModuleSpecifierPreferenceEnding": { + "type": "string", + "enum": [ + "auto", + "minimal", + "index", + "js" + ], + "title": "Import module specifier ending", + "default": "auto" + }, + "includeCompletionsForModuleExports": { + "type": "boolean", + "title": "Include completions for module exports", + "description": "If enabled, TypeScript will search through all external modules' exports and add them to the completions list. This affects lone identifier completions but not completions on the right hand side of obj.", + "default": true + }, + "includeCompletionsForImportStatements": { + "type": "boolean", + "title": "Include completions for import statements", + "description": "Enables auto-import-style completions on partially-typed import statements. E.g., allows import write| to be completed to import { writeFile } from 'fs'.", + "default": true + }, + "includeCompletionsWithSnippetText": { + "type": "boolean", + "title": "Include completions with snippet text", + "description": "Allows completions to be formatted with snippet text.", + "default": true + }, + "includeCompletionsWithInsertText": { + "type": "boolean", + "title": "Include completions with insert text", + "description": "If enabled, the completion list will include completions with invalid identifier names. For those the insertText and replacementSpan properties will be set to change from .x property access to ['x'].", + "default": true + }, + "includeAutomaticOptionalChainCompletions": { + "type": "boolean", + "title": "Include automatic optional chain completions", + "description": "Unless this option is false, or includeCompletionsWithInsertText is not enabled, member completion lists triggered with . will include entries on potentially-null and potentially-undefined values, with insertion text to replace preceding . tokens with ?.", + "default": true + }, + "includeCompletionsWithClassMemberSnippets": { + "type": "boolean", + "title": "Include completions with class member snippets", + "description": "If enabled, completions for class members (e.g. methods and properties) will include a whole declaration for the member. E.g., class A { f| } could be completed to class A { foo(): number {} }, instead of class A { foo }.", + "default": true + }, + "includeCompletionsWithObjectLiteralMethodSnippets": { + "type": "boolean", + "title": "Include completions with object literal method snippets", + "description": "If enabled, object literal methods will have a method declaration completion entry in addition to the regular completion entry containing just the method name. E.g., const objectLiteral: T = { f| } could be completed to const objectLiteral: T = { foo(): void {} }, in addition to const objectLiteral: T = { foo }.", + "default": true + }, + "includeInlayEnumMemberValueHints": { + "type": "boolean", + "title": "Include inlay enum member value hints", + "default": false + }, + "includeInlayFunctionLikeReturnTypeHints": { + "type": "boolean", + "title": "Include inlay function-like return type hints", + "default": false + }, + "includeInlayFunctionParameterTypeHints": { + "type": "boolean", + "title": "Include inlay function parameter type hints", + "default": false + }, + "includeInlayParameterNameHints": { + "type": "string", + "enum": [ + "none", + "literals", + "all" + ], + "title": "Include inlay parameter name hints", + "default": "none" + }, + "includeInlayParameterNameHintsWhenArgumentMatchesName": { + "type": "boolean", + "title": "Include inlay parameter name hints when argument matches name", + "default": false + }, + "includeInlayPropertyDeclarationTypeHints": { + "type": "boolean", + "title": "Include inlay property declaration type hints", + "default": false + }, + "includeInlayVariableTypeHints": { + "type": "boolean", + "title": "Include inlay variable type hints", + "default": false + }, + "includeInlayVariableTypeHintsWhenTypeMatchesName": { + "type": "boolean", + "title": "Include inlay variable type hints when type matches name", + "default": false + }, + "includePackageJsonAutoImports": { + "type": "string", + "enum": [ + "auto", + "on", + "off" + ], + "title": "Include package.json auto-imports", + "default": "auto" + }, + "interactiveInlayHints": { + "type": "boolean", + "title": "Interactive inlay hints", + "default": true + }, + "jsxAttributeCompletionStyle": { + "type": "string", + "enum": [ + "auto", + "braces", + "non" + ], + "title": "JSX attribute completion style", + "default": "auto" + }, + "lazyConfiguredProjectsFromExternalProject": { + "type": "boolean", + "title": "Lazy-configured projects from external project", + "default": false + }, + "organizeImportsIgnoreCase": { + "type": "object", + "oneOf": [ + { + "type": "boolean" + }, + { + "type": "string", + "enum": [ + "auto" + ] + } + ], + "title": "Organize imports ignore case", + "description": "Indicates whether imports should be organized in a case-insensitive manner.", + "default": "auto" + }, + "organizeImportsCollation": { + "type": "string", + "enum": [ + "ordinal", + "unicode" + ], + "title": "Organize imports collation", + "description": "Indicates whether imports should be organized via an 'ordinal' (binary) comparison using the numeric value of their code points, or via 'unicode' collation (via the Unicode Collation Algorithm) using rules associated with the locale specified in organizeImportsCollationLocale.", + "default": "ordinal" + }, + "organizeImportsCollationLocale": { + "type": "string", + "title": "Organize imports collation locale", + "description": "Indicates the locale to use for 'unicode' collation. If not specified, the locale 'en' is used as an invariant for the sake of consistent sorting. Use 'auto' to use the detected UI locale. This preference is ignored if organizeImportsCollation is not 'unicode'.", + "default": "en" + }, + "organizeImportsNumericCollation": { + "type": "boolean", + "title": "Organize imports numeric collation", + "description": "Indicates whether numeric collation should be used for digit sequences in strings. When true, will collate strings such that a1z < a2z < a100z. When false, will collate strings such that a1z < a100z < a2z. This preference is ignored if organizeImportsCollation is not 'unicode'.", + "default": false + }, + "organizeImportsAccentCollation": { + "type": "boolean", + "title": "Organize imports accent collation", + "description": "Indicates whether accents and other diacritic marks are considered unequal for the purpose of collation. When true, characters with accents and other diacritics will be collated in the order defined by the locale specified in organizeImportsCollationLocale. This preference is ignored if organizeImportsCollation is not 'unicode'.", + "default": true + }, + "organizeImportsCaseFirst": { + "type": "object", + "oneOf": [ + { + "type": "boolean" + }, + { + "type": "string", + "enum": [ + "upper", + "lower" + ] + } + ], + "title": "Organize imports case first", + "description": "Indicates whether upper case or lower case should sort first. When false, the default order for the locale specified in organizeImportsCollationLocale is used. This preference is ignored if organizeImportsCollation is not 'unicode'. This preference is also ignored if we are using case-insensitive sorting, which occurs when organizeImportsIgnoreCase is true, or if organizeImportsIgnoreCase is 'auto' and the auto-detected case sensitivity is determined to be case-insensitive.", + "default": false + }, + "providePrefixAndSuffixTextForRename": { + "type": "boolean", + "title": "Provide prefix and suffix text for rename", + "default": true + }, + "provideRefactorNotApplicableReason": { + "type": "boolean", + "title": "Provide refactor not-applicable reason", + "default": true + }, + "quotePreference": { + "type": "string", + "enum": [ + "auto", + "double", + "single" + ], + "title": "Quote preference", + "default": "auto" + }, + "useLabelDetailsInCompletionEntries": { + "type": "boolean", + "title": "Use label details in completion entries", + "description": "Indicates whether CompletionEntry.labelDetails completion entry label details are supported. If not, contents of labelDetails may be included in the CompletionEntry.name property. Only supported if the client supports textDocument.completion.completionItem.labelDetailsSupport capability and a compatible TypeScript version is used.", + "default": true + } + } + } + } + } + } +} \ No newline at end of file diff --git a/src/main/resources/templates/vscode-css-language-server/settings.schema.json b/src/main/resources/templates/vscode-css-language-server/settings.schema.json new file mode 100644 index 000000000..03f9c8bb0 --- /dev/null +++ b/src/main/resources/templates/vscode-css-language-server/settings.schema.json @@ -0,0 +1,903 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "LSP4IJ/vscode-css-language-server/settings.schema.json", + "title": "LSP4IJ VSCode CSS language server settings JSON schema", + "description": "JSON schema for VSCode CSS language server settings.", + "type": "object", + "additionalProperties": false, + "properties": { + "css": { + "type": "object", + "properties": { + "customData": { + "type": "array", + "default": [], + "items": { + "type": "string" + }, + "description": "A list of relative file paths pointing to JSON files following the [custom data format](https://github.com/microsoft/vscode-css-languageservice/blob/master/docs/customData.md).\n\nVS Code loads custom data on startup to enhance its CSS support for CSS custom properties (variables), at-rules, pseudo-classes, and pseudo-elements you specify in the JSON files.\n\nThe file paths are relative to workspace and only workspace folder settings are considered." + }, + "completion": { + "type": "object", + "properties": { + "triggerPropertyValueCompletion": { + "type": "boolean", + "default": true, + "description": "By default, VS Code triggers property value completion after selecting a CSS property. Use this setting to disable this behavior." + }, + "completePropertyWithSemicolon": { + "type": "boolean", + "default": true, + "description": "Insert semicolon at end of line when completing CSS properties." + } + } + }, + "validate": { + "type": "boolean", + "default": true, + "description": "Enables or disables all validations." + }, + "hover": { + "type": "object", + "properties": { + "documentation": { + "type": "boolean", + "default": true, + "description": "Show property and value documentation in CSS hovers." + }, + "references": { + "type": "boolean", + "default": true, + "description": "Show references to MDN in CSS hovers." + } + } + }, + "lint": { + "type": "object", + "properties": { + "compatibleVendorPrefixes": { + "type": "string", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "description": "When using a vendor-specific prefix make sure to also include all other vendor-specific properties." + }, + "vendorPrefix": { + "type": "string", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "warning", + "description": "When using a vendor-specific prefix, also include the standard property." + }, + "duplicateProperties": { + "type": "string", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "description": "Do not use duplicate style definitions." + }, + "emptyRules": { + "type": "string", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "warning", + "description": "Do not use empty rulesets." + }, + "importStatement": { + "type": "string", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "description": "Import statements do not load in parallel." + }, + "boxModel": { + "type": "string", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "description": "Do not use `width` or `height` when using `padding` or `border`." + }, + "universalSelector": { + "type": "string", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "description": "The universal selector (`*`) is known to be slow." + }, + "zeroUnits": { + "type": "string", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "description": "No unit for zero needed." + }, + "fontFaceProperties": { + "type": "string", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "warning", + "description": "`@font-face` rule must define `src` and `font-family` properties." + }, + "hexColorLength": { + "type": "string", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "error", + "description": "Hex colors must consist of 3, 4, 6 or 8 hex numbers." + }, + "argumentsInColorFunction": { + "type": "string", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "error", + "description": "Invalid number of parameters." + }, + "unknownProperties": { + "type": "string", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "warning", + "description": "Unknown property." + }, + "validProperties": { + "type": "array", + "uniqueItems": true, + "items": { + "type": "string" + }, + "default": [], + "description": "A list of properties that are not validated against the `unknownProperties` rule." + }, + "ieHack": { + "type": "string", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "description": "IE hacks are only necessary when supporting IE7 and older." + }, + "unknownVendorSpecificProperties": { + "type": "string", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "description": "Unknown vendor specific property." + }, + "propertyIgnoredDueToDisplay": { + "type": "string", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "warning", + "description": "Property is ignored due to the display. E.g. with `display: inline`, the `width`, `height`, `margin-top`, `margin-bottom`, and `float` properties have no effect." + }, + "important": { + "type": "string", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "description": "Avoid using `!important`. It is an indication that the specificity of the entire CSS has gotten out of control and needs to be refactored." + }, + "float": { + "type": "string", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "description": "Avoid using `float`. Floats lead to fragile CSS that is easy to break if one aspect of the layout changes." + }, + "idSelector": { + "type": "string", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "description": "Selectors should not contain IDs because these rules are too tightly coupled with the HTML." + }, + "unknownAtRules": { + "type": "string", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "warning", + "description": "Unknown at-rule." + } + } + }, + "trace": { + "type": "object", + "properties": { + "server": { + "type": "string", + "enum": [ + "off", + "messages", + "verbose" + ], + "default": "off", + "description": "Traces the communication between VS Code and the CSS language server." + } + } + }, + "format": { + "type": "object", + "properties": { + "enable": { + "type": "boolean", + "default": true, + "description": "Enable/disable default CSS formatter." + }, + "newlineBetweenSelectors": { + "type": "boolean", + "default": true, + "description": "Separate selectors with a new line." + }, + "newlineBetweenRules": { + "type": "boolean", + "default": true, + "description": "Separate rulesets by a blank line." + }, + "spaceAroundSelectorSeparator": { + "type": "boolean", + "default": false, + "description": "Ensure a space character around selector separators \u0027\u003e\u0027, \u0027+\u0027, \u0027~\u0027 (e.g. `a \u003e b`)." + }, + "braceStyle": { + "type": "string", + "default": "collapse", + "enum": [ + "collapse", + "expand" + ], + "description": "Put braces on the same line as rules (`collapse`) or put braces on own line (`expand`)." + }, + "preserveNewLines": { + "type": "boolean", + "default": true, + "description": "Whether existing line breaks before elements should be preserved." + }, + "maxPreserveNewLines": { + "type": [ + "number", + "null" + ], + "description": "Maximum number of line breaks to be preserved in one chunk, when `#css.format.preserveNewLines#` is enabled." + } + } + } + } + }, + "scss": { + "type": "object", + "properties": { + "completion": { + "type": "object", + "properties": { + "triggerPropertyValueCompletion": { + "type": "boolean", + "default": true, + "description": "By default, VS Code triggers property value completion after selecting a CSS property. Use this setting to disable this behavior." + }, + "completePropertyWithSemicolon": { + "type": "boolean", + "default": true, + "description": "Insert semicolon at end of line when completing CSS properties." + } + } + }, + "validate": { + "type": "boolean", + "default": true, + "description": "Enables or disables all validations." + }, + "hover": { + "type": "object", + "properties": { + "documentation": { + "type": "boolean", + "default": true, + "description": "Show property and value documentation in SCSS hovers." + }, + "references": { + "type": "boolean", + "default": true, + "description": "Show references to MDN in SCSS hovers." + } + } + }, + "lint": { + "type": "object", + "properties": { + "compatibleVendorPrefixes": { + "type": "string", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "description": "When using a vendor-specific prefix make sure to also include all other vendor-specific properties." + }, + "vendorPrefix": { + "type": "string", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "warning", + "description": "When using a vendor-specific prefix, also include the standard property." + }, + "duplicateProperties": { + "type": "string", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "description": "Do not use duplicate style definitions." + }, + "emptyRules": { + "type": "string", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "warning", + "description": "Do not use empty rulesets." + }, + "importStatement": { + "type": "string", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "description": "Import statements do not load in parallel." + }, + "boxModel": { + "type": "string", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "description": "Do not use `width` or `height` when using `padding` or `border`." + }, + "universalSelector": { + "type": "string", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "description": "The universal selector (`*`) is known to be slow." + }, + "zeroUnits": { + "type": "string", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "description": "No unit for zero needed." + }, + "fontFaceProperties": { + "type": "string", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "warning", + "description": "`@font-face` rule must define `src` and `font-family` properties." + }, + "hexColorLength": { + "type": "string", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "error", + "description": "Hex colors must consist of 3, 4, 6 or 8 hex numbers." + }, + "argumentsInColorFunction": { + "type": "string", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "error", + "description": "Invalid number of parameters." + }, + "unknownProperties": { + "type": "string", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "warning", + "description": "Unknown property." + }, + "validProperties": { + "type": "array", + "uniqueItems": true, + "items": { + "type": "string" + }, + "default": [], + "description": "A list of properties that are not validated against the `unknownProperties` rule." + }, + "ieHack": { + "type": "string", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "description": "IE hacks are only necessary when supporting IE7 and older." + }, + "unknownVendorSpecificProperties": { + "type": "string", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "description": "Unknown vendor specific property." + }, + "propertyIgnoredDueToDisplay": { + "type": "string", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "warning", + "description": "Property is ignored due to the display. E.g. with `display: inline`, the `width`, `height`, `margin-top`, `margin-bottom`, and `float` properties have no effect." + }, + "important": { + "type": "string", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "description": "Avoid using `!important`. It is an indication that the specificity of the entire CSS has gotten out of control and needs to be refactored." + }, + "float": { + "type": "string", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "description": "Avoid using `float`. Floats lead to fragile CSS that is easy to break if one aspect of the layout changes." + }, + "idSelector": { + "type": "string", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "description": "Selectors should not contain IDs because these rules are too tightly coupled with the HTML." + }, + "unknownAtRules": { + "type": "string", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "warning", + "description": "Unknown at-rule." + } + } + }, + "format": { + "type": "object", + "properties": { + "enable": { + "type": "boolean", + "default": true, + "description": "Enable/disable default SCSS formatter." + }, + "newlineBetweenSelectors": { + "type": "boolean", + "default": true, + "description": "Separate selectors with a new line." + }, + "newlineBetweenRules": { + "type": "boolean", + "default": true, + "description": "Separate rulesets by a blank line." + }, + "spaceAroundSelectorSeparator": { + "type": "boolean", + "default": false, + "description": "Ensure a space character around selector separators \u0027\u003e\u0027, \u0027+\u0027, \u0027~\u0027 (e.g. `a \u003e b`)." + }, + "braceStyle": { + "type": "string", + "default": "collapse", + "enum": [ + "collapse", + "expand" + ], + "description": "Put braces on the same line as rules (`collapse`) or put braces on own line (`expand`)." + }, + "preserveNewLines": { + "type": "boolean", + "default": true, + "description": "Whether existing line breaks before elements should be preserved." + }, + "maxPreserveNewLines": { + "type": [ + "number", + "null" + ], + "description": "Maximum number of line breaks to be preserved in one chunk, when `#scss.format.preserveNewLines#` is enabled." + } + } + } + } + }, + "less": { + "type": "object", + "properties": { + "completion": { + "type": "object", + "properties": { + "triggerPropertyValueCompletion": { + "type": "boolean", + "default": true, + "description": "By default, VS Code triggers property value completion after selecting a CSS property. Use this setting to disable this behavior." + }, + "completePropertyWithSemicolon": { + "type": "boolean", + "default": true, + "description": "Insert semicolon at end of line when completing CSS properties." + } + } + }, + "validate": { + "type": "boolean", + "default": true, + "description": "Enables or disables all validations." + }, + "hover": { + "type": "object", + "properties": { + "documentation": { + "type": "boolean", + "default": true, + "description": "Show property and value documentation in LESS hovers." + }, + "references": { + "type": "boolean", + "default": true, + "description": "Show references to MDN in LESS hovers." + } + } + }, + "lint": { + "type": "object", + "properties": { + "compatibleVendorPrefixes": { + "type": "string", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "description": "When using a vendor-specific prefix make sure to also include all other vendor-specific properties." + }, + "vendorPrefix": { + "type": "string", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "warning", + "description": "When using a vendor-specific prefix, also include the standard property." + }, + "duplicateProperties": { + "type": "string", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "description": "Do not use duplicate style definitions." + }, + "emptyRules": { + "type": "string", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "warning", + "description": "Do not use empty rulesets." + }, + "importStatement": { + "type": "string", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "description": "Import statements do not load in parallel." + }, + "boxModel": { + "type": "string", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "description": "Do not use `width` or `height` when using `padding` or `border`." + }, + "universalSelector": { + "type": "string", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "description": "The universal selector (`*`) is known to be slow." + }, + "zeroUnits": { + "type": "string", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "description": "No unit for zero needed." + }, + "fontFaceProperties": { + "type": "string", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "warning", + "description": "`@font-face` rule must define `src` and `font-family` properties." + }, + "hexColorLength": { + "type": "string", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "error", + "description": "Hex colors must consist of 3, 4, 6 or 8 hex numbers." + }, + "argumentsInColorFunction": { + "type": "string", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "error", + "description": "Invalid number of parameters." + }, + "unknownProperties": { + "type": "string", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "warning", + "description": "Unknown property." + }, + "validProperties": { + "type": "array", + "uniqueItems": true, + "items": { + "type": "string" + }, + "default": [], + "description": "A list of properties that are not validated against the `unknownProperties` rule." + }, + "ieHack": { + "type": "string", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "description": "IE hacks are only necessary when supporting IE7 and older." + }, + "unknownVendorSpecificProperties": { + "type": "string", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "description": "Unknown vendor specific property." + }, + "propertyIgnoredDueToDisplay": { + "type": "string", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "warning", + "description": "Property is ignored due to the display. E.g. with `display: inline`, the `width`, `height`, `margin-top`, `margin-bottom`, and `float` properties have no effect." + }, + "important": { + "type": "string", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "description": "Avoid using `!important`. It is an indication that the specificity of the entire CSS has gotten out of control and needs to be refactored." + }, + "float": { + "type": "string", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "description": "Avoid using `float`. Floats lead to fragile CSS that is easy to break if one aspect of the layout changes." + }, + "idSelector": { + "type": "string", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "description": "Selectors should not contain IDs because these rules are too tightly coupled with the HTML." + }, + "unknownAtRules": { + "type": "string", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "warning", + "description": "Unknown at-rule." + } + } + }, + "format": { + "type": "object", + "properties": { + "enable": { + "type": "boolean", + "default": true, + "description": "Enable/disable default LESS formatter." + }, + "newlineBetweenSelectors": { + "type": "boolean", + "default": true, + "description": "Separate selectors with a new line." + }, + "newlineBetweenRules": { + "type": "boolean", + "default": true, + "description": "Separate rulesets by a blank line." + }, + "spaceAroundSelectorSeparator": { + "type": "boolean", + "default": false, + "description": "Ensure a space character around selector separators \u0027\u003e\u0027, \u0027+\u0027, \u0027~\u0027 (e.g. `a \u003e b`)." + }, + "braceStyle": { + "type": "string", + "default": "collapse", + "enum": [ + "collapse", + "expand" + ], + "description": "Put braces on the same line as rules (`collapse`) or put braces on own line (`expand`)." + }, + "preserveNewLines": { + "type": "boolean", + "default": true, + "description": "Whether existing line breaks before elements should be preserved." + }, + "maxPreserveNewLines": { + "type": [ + "number", + "null" + ], + "description": "Maximum number of line breaks to be preserved in one chunk, when `#less.format.preserveNewLines#` is enabled." + } + } + } + } + } + } +} \ 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 806673f19..9c17c94ed 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 @@ -39,6 +39,7 @@ public void testBasicUserDefinedLsSerialization() { false, "", "", + "", ""); Gson gson = new GsonBuilder() @@ -62,6 +63,7 @@ public void testLanguageMapping() { false, "", "", + "", ""); lsDef.getLanguageMappings().put(Language.ANY, "testing"); @@ -89,6 +91,7 @@ public void testFilePatternsMapping() { false, "", "", + "", ""); FileNameMatcher fileNameMatcher1 = new ExtensionFileNameMatcher("rs"); FileNameMatcher fileNameMatcher2 = new WildcardFileNameMatcher("*kt"); @@ -123,6 +126,7 @@ public void testFileTypeMappings() { false, "", "", + "", ""); FileTypeManager fileTypeManager = FileTypeManager.getInstance(); FileType fileType = fileTypeManager.getFileTypeByExtension("any"); 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 f7866173c..fd54dcc40 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 @@ -110,6 +110,7 @@ private UserDefinedLanguageServerDefinition createUserDefinedLanguageServerDefin false, null, null, + null, null); } } \ No newline at end of file