diff --git a/org.eclipse.wildwebdeveloper.tests/src/org/eclipse/wildwebdeveloper/tests/TestJSONResultLimitPreference.java b/org.eclipse.wildwebdeveloper.tests/src/org/eclipse/wildwebdeveloper/tests/TestJSONResultLimitPreference.java new file mode 100644 index 0000000000..21e1b18887 --- /dev/null +++ b/org.eclipse.wildwebdeveloper.tests/src/org/eclipse/wildwebdeveloper/tests/TestJSONResultLimitPreference.java @@ -0,0 +1,113 @@ +/******************************************************************************* + * Copyright (c) 2026 Vogella GmbH and others. + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Lars Vogel (Vogella GmbH) - initial implementation + *******************************************************************************/ +package org.eclipse.wildwebdeveloper.tests; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.nio.charset.StandardCharsets; +import java.util.Objects; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.jface.text.IDocument; +import org.eclipse.lsp4e.LSPEclipseUtils; +import org.eclipse.lsp4e.LanguageServers; +import org.eclipse.lsp4e.LanguageServersRegistry; +import org.eclipse.lsp4j.DocumentSymbolParams; +import org.eclipse.lsp4j.ServerCapabilities; +import org.eclipse.lsp4j.TextDocumentIdentifier; +import org.eclipse.lsp4j.services.LanguageServer; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.ide.IDE; +import org.eclipse.ui.tests.harness.util.DisplayHelper; +import org.eclipse.ui.texteditor.AbstractTextEditor; +import org.eclipse.wildwebdeveloper.Activator; +import org.eclipse.wildwebdeveloper.json.JSonLanguageServer; +import org.eclipse.wildwebdeveloper.json.ui.preferences.JSonPreferenceServerConstants; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +@ExtendWith(AllCleanRule.class) +class TestJSONResultLimitPreference { + + private static final int KEYS = 60; + private static final int REDUCED_LIMIT = 5; + + private IProject project; + + @BeforeEach + void setUpProject() throws Exception { + this.project = ResourcesPlugin.getWorkspace().getRoot().getProject(getClass().getName() + System.nanoTime()); + project.create(null); + project.open(null); + } + + @AfterEach + void resetPreference() { + Activator.getDefault().getPreferenceStore().setValue( + JSonPreferenceServerConstants.JSON_PREFERENCES_MAXITEMSCOMPUTED, + JSonPreferenceServerConstants.MAXITEMSCOMPUTED_DEFAULT); + } + + @Test + void testResultLimitPreferenceAppliesWithoutRestart() throws Exception { + StringBuilder content = new StringBuilder("{\n"); + for (int i = 0; i < KEYS; i++) { + content.append(" \"key_").append(i).append("\": ").append(i).append(i == KEYS - 1 ? "\n" : ",\n"); + } + content.append("}\n"); + + IFile file = project.getFile("limit.json"); + file.create(content.toString().getBytes(StandardCharsets.UTF_8), true, false, null); + + AbstractTextEditor editor = (AbstractTextEditor) IDE.openEditor( + PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(), file, + "org.eclipse.ui.genericeditor.GenericEditor"); + IDocument document = LSPEclipseUtils.getDocument(editor); + DisplayHelper.sleep(2000); + + assertEquals(KEYS, countSymbols(document), "Default limit should not truncate this document"); + + Activator.getDefault().getPreferenceStore() + .setValue(JSonPreferenceServerConstants.JSON_PREFERENCES_MAXITEMSCOMPUTED, REDUCED_LIMIT); + DisplayHelper.sleep(1500); // allow the asynchronous preference broadcast + + // exact, so that an empty result does not silently satisfy the assertion + assertEquals(REDUCED_LIMIT, countSymbols(document), + "Lowering the limit should truncate the outline to exactly that many symbols"); + } + + private static int countSymbols(IDocument document) throws Exception { + DocumentSymbolParams params = new DocumentSymbolParams( + new TextDocumentIdentifier(LSPEclipseUtils.toUri(document).toString())); + return jsonLanguageServer(document).getTextDocumentService().documentSymbol(params).get(10, TimeUnit.SECONDS) + .size(); + } + + private static LanguageServer jsonLanguageServer(IDocument document) throws Exception { + return LanguageServers.forDocument(document) // + .withCapability(ServerCapabilities::getDocumentSymbolProvider) // + .collectAll((wrapper, ls) -> CompletableFuture.completedFuture( // + LanguageServersRegistry.getInstance() + .getDefinition(JSonLanguageServer.JSON_LANGUAGE_SERVER_ID) + .equals(wrapper.serverDefinition) ? ls : null)) // + .get(10, TimeUnit.SECONDS).stream() // + .filter(Objects::nonNull) // + .findFirst() // + .orElseThrow(() -> new AssertionError("Expected a running JSON language server for the document")); + } +} diff --git a/org.eclipse.wildwebdeveloper/META-INF/MANIFEST.MF b/org.eclipse.wildwebdeveloper/META-INF/MANIFEST.MF index 244554cef4..dbaf9f80c9 100644 --- a/org.eclipse.wildwebdeveloper/META-INF/MANIFEST.MF +++ b/org.eclipse.wildwebdeveloper/META-INF/MANIFEST.MF @@ -41,4 +41,6 @@ Export-Package: org.eclipse.wildwebdeveloper;x-friends:="org.eclipse.wildwebdeve org.eclipse.wildwebdeveloper.debug.npm;x-internal:=true, org.eclipse.wildwebdeveloper.markdown;x-friends:="org.eclipse.wildwebdeveloper.tests", org.eclipse.wildwebdeveloper.jsts;x-friends:="org.eclipse.wildwebdeveloper.tests", - org.eclipse.wildwebdeveloper.jsts.ui.preferences;x-friends:="org.eclipse.wildwebdeveloper.tests" + org.eclipse.wildwebdeveloper.jsts.ui.preferences;x-friends:="org.eclipse.wildwebdeveloper.tests", + org.eclipse.wildwebdeveloper.json;x-friends:="org.eclipse.wildwebdeveloper.tests", + org.eclipse.wildwebdeveloper.json.ui.preferences;x-friends:="org.eclipse.wildwebdeveloper.tests" diff --git a/org.eclipse.wildwebdeveloper/plugin.properties b/org.eclipse.wildwebdeveloper/plugin.properties index 2cfd9b95dd..178822972e 100644 --- a/org.eclipse.wildwebdeveloper/plugin.properties +++ b/org.eclipse.wildwebdeveloper/plugin.properties @@ -48,6 +48,7 @@ JSTSFormatterPreferencePage.name=Formatter # Markdown MarkdownPreferencePage.name=Markdown (Wild Web Developer) +JSonPreferencePage.name=JSON (Wild Web Developer) MarkdownProblem=Markdown Problem # YAML @@ -69,3 +70,4 @@ preferenceKeywords.scss=scss preferenceKeywords.sass=sass preferenceKeywords.html=html preferenceKeywords.markdown=markdown +preferenceKeywords.json=json diff --git a/org.eclipse.wildwebdeveloper/plugin.xml b/org.eclipse.wildwebdeveloper/plugin.xml index 9ad200e3bf..99ee8c9c08 100644 --- a/org.eclipse.wildwebdeveloper/plugin.xml +++ b/org.eclipse.wildwebdeveloper/plugin.xml @@ -131,6 +131,19 @@ + + + + + + + + + + diff --git a/org.eclipse.wildwebdeveloper/src/org/eclipse/wildwebdeveloper/json/JSonLanguageServer.java b/org.eclipse.wildwebdeveloper/src/org/eclipse/wildwebdeveloper/json/JSonLanguageServer.java index f27457de92..36279a0ce2 100644 --- a/org.eclipse.wildwebdeveloper/src/org/eclipse/wildwebdeveloper/json/JSonLanguageServer.java +++ b/org.eclipse.wildwebdeveloper/src/org/eclipse/wildwebdeveloper/json/JSonLanguageServer.java @@ -37,7 +37,7 @@ import org.eclipse.lsp4e.LanguageServers; import org.eclipse.lsp4e.LanguageServersRegistry; import org.eclipse.lsp4e.LanguageServersRegistry.LanguageServerDefinition; -import org.eclipse.lsp4e.server.ProcessStreamConnectionProvider; +import org.eclipse.lsp4j.DidChangeConfigurationParams; import org.eclipse.lsp4j.InitializeResult; import org.eclipse.lsp4j.jsonrpc.messages.Message; import org.eclipse.lsp4j.jsonrpc.messages.ResponseMessage; @@ -46,20 +46,26 @@ import org.eclipse.wildwebdeveloper.SchemaAssociationRegistry; import org.eclipse.wildwebdeveloper.SchemaAssociationsPreferenceInitializer; import org.eclipse.wildwebdeveloper.embedder.node.NodeJSManager; +import org.eclipse.wildwebdeveloper.json.ui.preferences.JSonPreferenceServerConstants; +import org.eclipse.wildwebdeveloper.ui.preferences.ProcessStreamConnectionProviderWithPreference; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; @SuppressWarnings("restriction") -public class JSonLanguageServer extends ProcessStreamConnectionProvider { +public class JSonLanguageServer extends ProcessStreamConnectionProviderWithPreference { public final static String SCHEMA_EXT = "org.eclipse.wildwebdeveloper.json.schema"; //$NON-NLS-1$ public final static String PATTERN_ATTR = "pattern"; //$NON-NLS-1$ public final static String URL_ATTR = "url"; //$NON-NLS-1$ + public static final String JSON_LANGUAGE_SERVER_ID = "org.eclipse.wildwebdeveloper.json"; //$NON-NLS-1$ + + private static final String[] SUPPORTED_SECTIONS = { "json" }; //$NON-NLS-1$ + private static final IPreferenceStore PREFERENCE_STORE = Activator.getDefault().getPreferenceStore(); private static final LanguageServerDefinition JSON_LS_DEFINITION = LanguageServersRegistry.getInstance() - .getDefinition("org.eclipse.wildwebdeveloper.json"); + .getDefinition(JSON_LANGUAGE_SERVER_ID); private static final IPropertyChangeListener PROPERTY_CHANGE_LISTENER = new IPropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent event) { @@ -74,6 +80,7 @@ public void propertyChange(PropertyChangeEvent event) { }; public JSonLanguageServer() { + super(JSON_LANGUAGE_SERVER_ID, PREFERENCE_STORE, SUPPORTED_SECTIONS); List commands = new ArrayList<>(); commands.add(NodeJSManager.getNodeJsLocation().getAbsolutePath()); try { @@ -96,10 +103,16 @@ public void handleMessage(Message message, LanguageServer languageServer, URI ro // Language server side. JSonLanguageServerInterface server = (JSonLanguageServerInterface) languageServer; server.sendJSonchemaAssociations(getSchemaAssociations()); + server.getWorkspaceService().didChangeConfiguration(new DidChangeConfigurationParams(createSettings())); } } } + @Override + protected Object createSettings() { + return JSonPreferenceServerConstants.getGlobalSettings(); + } + private static Map> getSchemaAssociations() { Map> associations = new HashMap<>(); fillSchemaAssociationsFromPreferenceStore(associations); diff --git a/org.eclipse.wildwebdeveloper/src/org/eclipse/wildwebdeveloper/json/ui/Messages.java b/org.eclipse.wildwebdeveloper/src/org/eclipse/wildwebdeveloper/json/ui/Messages.java new file mode 100644 index 0000000000..ad31f11c5f --- /dev/null +++ b/org.eclipse.wildwebdeveloper/src/org/eclipse/wildwebdeveloper/json/ui/Messages.java @@ -0,0 +1,28 @@ +/******************************************************************************* + * Copyright (c) 2026 Vogella GmbH and others. + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Lars Vogel (Vogella GmbH) - initial implementation + *******************************************************************************/ +package org.eclipse.wildwebdeveloper.json.ui; + +import org.eclipse.osgi.util.NLS; + +/** + * JSON messages keys. + * + */ +public class Messages extends NLS { + + // --------- JSON Main preference page + public static String JSonPreferencePage_maxItemsComputed; + + static { + NLS.initializeMessages("org.eclipse.wildwebdeveloper.json.ui.messages", Messages.class); //$NON-NLS-1$ + } +} diff --git a/org.eclipse.wildwebdeveloper/src/org/eclipse/wildwebdeveloper/json/ui/messages.properties b/org.eclipse.wildwebdeveloper/src/org/eclipse/wildwebdeveloper/json/ui/messages.properties new file mode 100644 index 0000000000..0a1aabfc46 --- /dev/null +++ b/org.eclipse.wildwebdeveloper/src/org/eclipse/wildwebdeveloper/json/ui/messages.properties @@ -0,0 +1,14 @@ +#/******************************************************************************* +# * Copyright (c) 2026 Vogella GmbH and others. +# * This program and the accompanying materials are made +# * available under the terms of the Eclipse Public License 2.0 +# * which is available at https://www.eclipse.org/legal/epl-2.0/ +# * +# * SPDX-License-Identifier: EPL-2.0 +# * +# * Contributors: +# * Lars Vogel (Vogella GmbH) - initial implementation +# *******************************************************************************/ + +# Preference main page +JSonPreferencePage_maxItemsComputed=The maximum number of outline symbols and folding\nregions computed (limited for performance reasons, 0 means no limit). diff --git a/org.eclipse.wildwebdeveloper/src/org/eclipse/wildwebdeveloper/json/ui/preferences/JSonPreferenceInitializer.java b/org.eclipse.wildwebdeveloper/src/org/eclipse/wildwebdeveloper/json/ui/preferences/JSonPreferenceInitializer.java new file mode 100644 index 0000000000..ddbeb66e95 --- /dev/null +++ b/org.eclipse.wildwebdeveloper/src/org/eclipse/wildwebdeveloper/json/ui/preferences/JSonPreferenceInitializer.java @@ -0,0 +1,27 @@ +/******************************************************************************* + * Copyright (c) 2026 Vogella GmbH and others. + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Lars Vogel (Vogella GmbH) - initial implementation + *******************************************************************************/ +package org.eclipse.wildwebdeveloper.json.ui.preferences; + +import org.eclipse.core.runtime.preferences.AbstractPreferenceInitializer; + +/** + * JSON preference initializer. + * + */ +public class JSonPreferenceInitializer extends AbstractPreferenceInitializer { + + @Override + public void initializeDefaultPreferences() { + JSonPreferenceServerConstants.initializeDefaultPreferences(); + } + +} diff --git a/org.eclipse.wildwebdeveloper/src/org/eclipse/wildwebdeveloper/json/ui/preferences/JSonPreferencePage.java b/org.eclipse.wildwebdeveloper/src/org/eclipse/wildwebdeveloper/json/ui/preferences/JSonPreferencePage.java new file mode 100644 index 0000000000..01fb424d08 --- /dev/null +++ b/org.eclipse.wildwebdeveloper/src/org/eclipse/wildwebdeveloper/json/ui/preferences/JSonPreferencePage.java @@ -0,0 +1,46 @@ +/******************************************************************************* + * Copyright (c) 2026 Vogella GmbH and others. + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Lars Vogel (Vogella GmbH) - initial implementation + *******************************************************************************/ +package org.eclipse.wildwebdeveloper.json.ui.preferences; + +import static org.eclipse.wildwebdeveloper.json.ui.preferences.JSonPreferenceServerConstants.JSON_PREFERENCES_MAXITEMSCOMPUTED; + +import org.eclipse.jface.preference.FieldEditorPreferencePage; +import org.eclipse.jface.preference.IntegerFieldEditor; +import org.eclipse.ui.IWorkbench; +import org.eclipse.ui.IWorkbenchPreferencePage; +import org.eclipse.wildwebdeveloper.Activator; +import org.eclipse.wildwebdeveloper.json.ui.Messages; + +/** + * JSON main preference page. + * + */ +public class JSonPreferencePage extends FieldEditorPreferencePage implements IWorkbenchPreferencePage { + + public JSonPreferencePage() { + super(GRID); + } + + @Override + public void init(IWorkbench workbench) { + setPreferenceStore(Activator.getDefault().getPreferenceStore()); + } + + @Override + protected void createFieldEditors() { + IntegerFieldEditor maxItemsComputed = new IntegerFieldEditor(JSON_PREFERENCES_MAXITEMSCOMPUTED, + Messages.JSonPreferencePage_maxItemsComputed, getFieldEditorParent()); + maxItemsComputed.setValidRange(0, Integer.MAX_VALUE); + addField(maxItemsComputed); + } + +} diff --git a/org.eclipse.wildwebdeveloper/src/org/eclipse/wildwebdeveloper/json/ui/preferences/JSonPreferenceServerConstants.java b/org.eclipse.wildwebdeveloper/src/org/eclipse/wildwebdeveloper/json/ui/preferences/JSonPreferenceServerConstants.java new file mode 100644 index 0000000000..b4d3c3782b --- /dev/null +++ b/org.eclipse.wildwebdeveloper/src/org/eclipse/wildwebdeveloper/json/ui/preferences/JSonPreferenceServerConstants.java @@ -0,0 +1,71 @@ +/******************************************************************************* + * Copyright (c) 2026 Vogella GmbH and others. + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Lars Vogel (Vogella GmbH) - initial implementation + *******************************************************************************/ +package org.eclipse.wildwebdeveloper.json.ui.preferences; + +import org.eclipse.jface.preference.IPreferenceStore; +import org.eclipse.wildwebdeveloper.Activator; +import org.eclipse.wildwebdeveloper.ui.preferences.Settings; + +/** + * JSON preference server constants. + * + */ +public class JSonPreferenceServerConstants { + + private static final String JSON_SECTION = "json"; //$NON-NLS-1$ + + /** + * Equivalent of VSCode's json.maxItemsComputed. Also drives the folding limits + * below, as VSCode does. 0 means no limit. + */ + public static final String JSON_PREFERENCES_MAXITEMSCOMPUTED = "json.resultLimit"; //$NON-NLS-1$ + + private static final String JSON_PREFERENCES_JSON_FOLDINGLIMIT = "json.jsonFoldingLimit"; //$NON-NLS-1$ + private static final String JSON_PREFERENCES_JSONC_FOLDINGLIMIT = "json.jsoncFoldingLimit"; //$NON-NLS-1$ + private static final String JSON_PREFERENCES_VALIDATE_ENABLE = "json.validate.enable"; //$NON-NLS-1$ + private static final String JSON_PREFERENCES_FORMAT_ENABLE = "json.format.enable"; //$NON-NLS-1$ + private static final String HTTP_PREFERENCES_PROXYSTRICTSSL = "http.proxyStrictSSL"; //$NON-NLS-1$ + + public static final int MAXITEMSCOMPUTED_DEFAULT = 5000; + + /** + * The server re-reads all of its settings from every notification, so entries + * that only restate a default still have to be sent: without "validate.enable" + * it turns validation off, and without "http.proxyStrictSSL" it stops verifying + * certificates when downloading schemas. + */ + public static Settings getGlobalSettings() { + IPreferenceStore store = Activator.getDefault().getPreferenceStore(); + Settings settings = new Settings(store); + + int maxItemsComputed = store.getInt(JSON_PREFERENCES_MAXITEMSCOMPUTED); + settings.fillSetting(JSON_PREFERENCES_MAXITEMSCOMPUTED, maxItemsComputed); + settings.fillSetting(JSON_PREFERENCES_JSON_FOLDINGLIMIT, maxItemsComputed); + settings.fillSetting(JSON_PREFERENCES_JSONC_FOLDINGLIMIT, maxItemsComputed); + + settings.fillSetting(JSON_PREFERENCES_VALIDATE_ENABLE, Boolean.TRUE); + settings.fillSetting(JSON_PREFERENCES_FORMAT_ENABLE, Boolean.TRUE); + settings.fillSetting(HTTP_PREFERENCES_PROXYSTRICTSSL, Boolean.TRUE); + + return settings; + } + + public static void initializeDefaultPreferences() { + IPreferenceStore store = Activator.getDefault().getPreferenceStore(); + store.setDefault(JSON_PREFERENCES_MAXITEMSCOMPUTED, MAXITEMSCOMPUTED_DEFAULT); + } + + public static boolean isMatchJSonSection(String section) { + return Settings.isMatchSection(section, JSON_SECTION); + } + +}