From 934f22e4a9d1d112d191f5859ed95f720cfa5974 Mon Sep 17 00:00:00 2001 From: Shi Chen Date: Wed, 15 Mar 2023 16:06:50 +0800 Subject: [PATCH 1/2] Support VS Code formatter scheme Signed-off-by: Shi Chen --- .../internal/handlers/FormatterHandler.java | 2 + .../preferences/FormatterPreferences.java | 144 ++++++++++++++++++ .../internal/preferences/Preferences.java | 83 ++++++++++ 3 files changed, 229 insertions(+) create mode 100644 org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/preferences/FormatterPreferences.java diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/FormatterHandler.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/FormatterHandler.java index 2b44431a97..668d7eb062 100644 --- a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/FormatterHandler.java +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/FormatterHandler.java @@ -38,6 +38,7 @@ import org.eclipse.jdt.internal.ui.preferences.formatter.ProfileVersionerCore; import org.eclipse.jdt.ls.core.internal.JDTUtils; import org.eclipse.jdt.ls.core.internal.JavaLanguageServerPlugin; +import org.eclipse.jdt.ls.core.internal.preferences.FormatterPreferences; import org.eclipse.jdt.ls.core.internal.preferences.PreferenceManager; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.Document; @@ -150,6 +151,7 @@ public static Map getOptions(FormattingOptions options, ICompila Map customOptions = options.entrySet().stream().filter(map -> chekIfValueIsNotNull(map.getValue())).collect(toMap(e -> e.getKey(), e -> getOptionValue(e.getValue()))); eclipseOptions.putAll(customOptions); + eclipseOptions.putAll(FormatterPreferences.toEclipseOptions(JavaLanguageServerPlugin.getPreferencesManager().getPreferences().getFormatterSettings())); Integer tabSize = options.getTabSize(); if (tabSize != null) { diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/preferences/FormatterPreferences.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/preferences/FormatterPreferences.java new file mode 100644 index 0000000000..e548341e50 --- /dev/null +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/preferences/FormatterPreferences.java @@ -0,0 +1,144 @@ +/******************************************************************************* + * Copyright (c) 2023 Red Hat Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Microsoft Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.jdt.ls.core.internal.preferences; + +import java.util.Map; +import java.util.stream.Collectors; + +import org.eclipse.jdt.core.formatter.DefaultCodeFormatterConstants; + +public class FormatterPreferences { + + // @formatter:off + // < JDTLS settings, eclipse settings > + private static Map eclipseOptions = Map.ofEntries( + Map.entry("lineSplit", DefaultCodeFormatterConstants.FORMATTER_LINE_SPLIT), + Map.entry("comment.line.length", DefaultCodeFormatterConstants.FORMATTER_COMMENT_LINE_LENGTH), + Map.entry("join.wrapped.lines", DefaultCodeFormatterConstants.FORMATTER_JOIN_WRAPPED_LINES), + Map.entry("use.on.off.tags", DefaultCodeFormatterConstants.FORMATTER_USE_ON_OFF_TAGS), + Map.entry("disabling.tag", DefaultCodeFormatterConstants.FORMATTER_DISABLING_TAG), + Map.entry("enabling.tag", DefaultCodeFormatterConstants.FORMATTER_ENABLING_TAG), + Map.entry("indent.parameter.description", DefaultCodeFormatterConstants.FORMATTER_COMMENT_INDENT_PARAMETER_DESCRIPTION), + Map.entry("indent.root.tags", DefaultCodeFormatterConstants.FORMATTER_COMMENT_INDENT_ROOT_TAGS), + Map.entry("align.tags.descriptions.grouped", DefaultCodeFormatterConstants.FORMATTER_COMMENT_ALIGN_TAGS_DESCREIPTIONS_GROUPED), + Map.entry("align.tags.names.descriptions", DefaultCodeFormatterConstants.FORMATTER_COMMENT_ALIGN_TAGS_NAMES_DESCRIPTIONS), + Map.entry("clear.blank.lines.in.javadoc.comment", DefaultCodeFormatterConstants.FORMATTER_COMMENT_CLEAR_BLANK_LINES_IN_JAVADOC_COMMENT), + Map.entry("blank.lines.between.import.groups", DefaultCodeFormatterConstants.FORMATTER_BLANK_LINES_BETWEEN_IMPORT_GROUPS), + Map.entry("format.line.comments", DefaultCodeFormatterConstants.FORMATTER_COMMENT_FORMAT_LINE_COMMENT), + Map.entry("format.block.comments", DefaultCodeFormatterConstants.FORMATTER_COMMENT_FORMAT_BLOCK_COMMENT), + Map.entry("format.javadoc.comments", DefaultCodeFormatterConstants.FORMATTER_COMMENT_FORMAT_JAVADOC_COMMENT), + Map.entry("keep.loop.body.block.on.one.line", DefaultCodeFormatterConstants.FORMATTER_KEEP_LOOP_BODY_BLOCK_ON_ONE_LINE), + Map.entry("keep.anonymous.type.declaration.on.one.line", DefaultCodeFormatterConstants.FORMATTER_KEEP_ANONYMOUS_TYPE_DECLARATION_ON_ONE_LINE), + Map.entry("keep.type.declaration.on.one.line", DefaultCodeFormatterConstants.FORMATTER_KEEP_TYPE_DECLARATION_ON_ONE_LINE), + Map.entry("keep.method.body.on.one.line", DefaultCodeFormatterConstants.FORMATTER_KEEP_METHOD_BODY_ON_ONE_LINE), + Map.entry("insert.space.after.closing.angle.bracket.in.type.arguments", DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_AFTER_CLOSING_ANGLE_BRACKET_IN_TYPE_ARGUMENTS), + Map.entry("insert.space.after.opening.brace.in.array.initializer", DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_AFTER_OPENING_BRACE_IN_ARRAY_INITIALIZER), + Map.entry("insert.space.before.closing.brace.in.array.initializer", DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_BEFORE_CLOSING_BRACE_IN_ARRAY_INITIALIZER), + Map.entry("brace.position.for.block", DefaultCodeFormatterConstants.FORMATTER_BRACE_POSITION_FOR_BLOCK), + Map.entry("alignment.for.enum.constants", DefaultCodeFormatterConstants.FORMATTER_ALIGNMENT_FOR_ENUM_CONSTANTS), + Map.entry("alignment.for.parameters.in.method.declaration", DefaultCodeFormatterConstants.FORMATTER_ALIGNMENT_FOR_PARAMETERS_IN_METHOD_DECLARATION) + ); + + // < JDTLS camelCase value, eclipse underline value> + private static Map valueMap = Map.ofEntries( + Map.entry("commonLines", "common_lines"), + Map.entry("separateLinesIfNotEmpty", "separate_lines_if_not_empty"), + Map.entry("separateLinesIfWrapped", "separate_lines_if_wrapped"), + Map.entry("separateLines", "separate_lines"), + Map.entry("preservePositions", "preserve_positions"), + Map.entry("never", "one_line_never"), + Map.entry("ifEmpty", "one_line_if_empty"), + Map.entry("ifSingleItem", "one_line_if_single_item"), + Map.entry("always", "one_line_always"), + Map.entry("preserve", "one_line_preserve"), + Map.entry("doNotInsert", "do not insert"), + Map.entry("endOfLine", "end_of_line"), + Map.entry("nextLine", "next_line"), + Map.entry("nextLineIndented", "next_line_indented"), + Map.entry("nextLineOnWrap", "next_line_on_wrap") + ); + // @formatter:on + + /** + * Convert known language server formatter options to eclipse formatter + * settings. + * + * @param lsOptions + * the given language server formatter options + * @return the converted eclipse formatter options + */ + public static Map toEclipseOptions(Map lsOptions) { + return lsOptions.entrySet().stream().filter(option -> eclipseOptions.containsKey(option.getKey())).collect(Collectors.toMap(option -> eclipseOptions.get(option.getKey()), option -> { + String value = option.getValue(); + if (valueMap.containsKey(value)) { + return valueMap.get(value); + } + return value; + })); + } + + /** + * Convert language server formatter alignment value to eclipse formatter + * alignment value. + * + * @param alignmentValue + * the given language server formatter alignment value + * @return the converted eclipse formatter alignment value + */ + public static String getEclipseAlignmentValue(Map alignmentValue) { + Object forceSplit = alignmentValue.getOrDefault("force.split", Boolean.FALSE); + Object indentationStyle = alignmentValue.getOrDefault("indentation.style", "indentDefault"); + Object wrappingStyle = alignmentValue.getOrDefault("wrapping.style", "compact"); + if (forceSplit instanceof Boolean forceSplitBoolean && indentationStyle instanceof String indentationStyleString && wrappingStyle instanceof String wrappingStyleString) { + int indentationStyleInt = 0; + switch (indentationStyleString) { + case "indentDefault": + indentationStyleInt = DefaultCodeFormatterConstants.INDENT_DEFAULT; + break; + case "indentOnColumn": + indentationStyleInt = DefaultCodeFormatterConstants.INDENT_ON_COLUMN; + break; + case "indentByOne": + indentationStyleInt = DefaultCodeFormatterConstants.INDENT_BY_ONE; + break; + default: + return null; + } + int wrappingStyleInt = 0; + switch (wrappingStyleString) { + case "noSplit": + wrappingStyleInt = DefaultCodeFormatterConstants.WRAP_NO_SPLIT; + break; + case "compact": + wrappingStyleInt = DefaultCodeFormatterConstants.WRAP_COMPACT; + break; + case "compactFirstBreak": + wrappingStyleInt = DefaultCodeFormatterConstants.WRAP_COMPACT_FIRST_BREAK; + break; + case "onePerLine": + wrappingStyleInt = DefaultCodeFormatterConstants.WRAP_ONE_PER_LINE; + break; + case "nextShifted": + wrappingStyleInt = DefaultCodeFormatterConstants.WRAP_NEXT_SHIFTED; + break; + case "nextPerLine": + wrappingStyleInt = DefaultCodeFormatterConstants.WRAP_NEXT_PER_LINE; + break; + default: + return null; + } + return DefaultCodeFormatterConstants.createAlignmentValue(forceSplitBoolean, wrappingStyleInt, indentationStyleInt); + } + return null; + } +} diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/preferences/Preferences.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/preferences/Preferences.java index 1a54866d94..582129ca01 100644 --- a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/preferences/Preferences.java +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/preferences/Preferences.java @@ -114,6 +114,10 @@ public class Preferences { */ public static final String JAVA_CONFIGURATION_RUNTIMES = "java.configuration.runtimes"; public static final List JAVA_CONFIGURATION_RUNTIMES_DEFAULT; + /** + * Specifies the java formatter scheme. + */ + public static final String JAVA_FORMATTER_SCHEME = "java.format.scheme"; /** * Specifies the file path or url to the formatter xml url. */ @@ -619,6 +623,8 @@ public class Preferences { private String formatterUrl; private String settingsUrl; private String formatterProfileName; + private Map formatterSettings; + private FormatterScheme formatterScheme; private Collection rootPaths; private Collection triggerFiles; private Collection projectConfigurations; @@ -722,6 +728,22 @@ static FeatureStatus fromString(String value, FeatureStatus defaultStatus) { } } + public static enum FormatterScheme { + eclipse; + + static FormatterScheme fromString(String value, FormatterScheme defaultValue) { + if (value != null) { + String val = value.toLowerCase(); + try { + return valueOf(val); + } catch (Exception e) { + //fall back to default severity + } + } + return defaultValue; + } + } + public static class ReferencedLibraries { private Set include; private Set exclude; @@ -850,6 +872,8 @@ public Preferences() { formatterUrl = null; settingsUrl = null; formatterProfileName = null; + formatterSettings = new HashMap<>(); + formatterScheme = FormatterScheme.eclipse; importOrder = JAVA_IMPORT_ORDER_DEFAULT; filteredTypes = JAVA_COMPLETION_FILTERED_TYPES_DEFAULT; parallelBuildsCount = PreferenceInitializer.PREF_MAX_CONCURRENT_BUILDS_DEFAULT; @@ -1122,6 +1146,52 @@ public static Preferences createFrom(Map configuration) { boolean javaFormatComments = getBoolean(configuration, JAVA_FORMAT_COMMENTS, true); prefs.setJavaFormatComments(javaFormatComments); + String formatterSchemeString = getString(configuration, JAVA_FORMATTER_SCHEME); + if (formatterSchemeString != null) { + FormatterScheme formatterScheme = FormatterScheme.fromString(formatterSchemeString, FormatterScheme.eclipse); + if (formatterScheme != null) { + prefs.setFormatterScheme(formatterScheme); + } + } else { + Object formatterSchemeValue = getValue(configuration, JAVA_FORMATTER_SCHEME); + if (formatterSchemeValue instanceof Map) { + Map formatterSchemeValueMap = (Map) formatterSchemeValue; + Object style = formatterSchemeValueMap.getOrDefault("style", FormatterScheme.eclipse.toString()); + if (style instanceof String schemeString) { + FormatterScheme formatterScheme = FormatterScheme.fromString(schemeString, FormatterScheme.eclipse); + if (formatterScheme != null) { + prefs.setFormatterScheme(formatterScheme); + } + } + Object path = formatterSchemeValueMap.getOrDefault("path", null); + if (path instanceof String pathString) { + prefs.setFormatterUrl(pathString); + } + Object profile = formatterSchemeValueMap.getOrDefault("profile", null); + if (profile instanceof String profileString) { + prefs.setFormatterProfileName(profileString); + } + Object configurations = formatterSchemeValueMap.getOrDefault("configurations", new HashMap<>()); + if (configurations instanceof Map) { + Map configurationMap = (Map) configurations; + Map formatterSettingsMap = new HashMap<>(); + configurationMap.forEach((k, v) -> { + Object value = v; + if (value instanceof Double d) { + value = d.intValue(); + } else if (value instanceof Map) { + Map valueMap = (Map) value; + value = FormatterPreferences.getEclipseAlignmentValue(valueMap); + } + if (value != null) { + formatterSettingsMap.put(String.valueOf(k), String.valueOf(value)); + } + }); + prefs.setFormatterSettings(formatterSettingsMap); + } + } + } + List javaImportOrder = getList(configuration, JAVA_IMPORT_ORDER_KEY, JAVA_IMPORT_ORDER_DEFAULT); prefs.setImportOrder(javaImportOrder); @@ -1288,6 +1358,15 @@ public Preferences setSettingsUrl(String settingsUrl) { return this; } + public Preferences setFormatterSettings(Map formatterSettings) { + this.formatterSettings = formatterSettings; + return this; + } + + public void setFormatterScheme(FormatterScheme formatterScheme) { + this.formatterScheme = formatterScheme; + } + public Preferences setResourceFilters(List resourceFilters) { this.resourceFilters = resourceFilters == null ? new ArrayList<>() : resourceFilters; return this; @@ -1607,6 +1686,10 @@ public URI getFormatterAsURI() { return asURI(formatterUrl); } + public Map getFormatterSettings() { + return this.formatterSettings; + } + public String getSettingsUrl() { return settingsUrl; } From c480ffc38256c4efa9ea1144025bf869f6032117 Mon Sep 17 00:00:00 2001 From: Shi Chen Date: Tue, 18 Apr 2023 08:59:26 +0800 Subject: [PATCH 2/2] add missing config Signed-off-by: Shi Chen --- .../jdt/ls/core/internal/preferences/FormatterPreferences.java | 1 + 1 file changed, 1 insertion(+) diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/preferences/FormatterPreferences.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/preferences/FormatterPreferences.java index e548341e50..b0579d7f43 100644 --- a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/preferences/FormatterPreferences.java +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/preferences/FormatterPreferences.java @@ -37,6 +37,7 @@ public class FormatterPreferences { Map.entry("format.line.comments", DefaultCodeFormatterConstants.FORMATTER_COMMENT_FORMAT_LINE_COMMENT), Map.entry("format.block.comments", DefaultCodeFormatterConstants.FORMATTER_COMMENT_FORMAT_BLOCK_COMMENT), Map.entry("format.javadoc.comments", DefaultCodeFormatterConstants.FORMATTER_COMMENT_FORMAT_JAVADOC_COMMENT), + Map.entry("parentheses.positions.in.method.invocation", DefaultCodeFormatterConstants.FORMATTER_PARENTHESES_POSITIONS_IN_METHOD_INVOCATION), Map.entry("keep.loop.body.block.on.one.line", DefaultCodeFormatterConstants.FORMATTER_KEEP_LOOP_BODY_BLOCK_ON_ONE_LINE), Map.entry("keep.anonymous.type.declaration.on.one.line", DefaultCodeFormatterConstants.FORMATTER_KEEP_ANONYMOUS_TYPE_DECLARATION_ON_ONE_LINE), Map.entry("keep.type.declaration.on.one.line", DefaultCodeFormatterConstants.FORMATTER_KEEP_TYPE_DECLARATION_ON_ONE_LINE),