diff --git a/launch/jdt.ls.socket-stream.launch b/launch/jdt.ls.socket-stream.launch index 362e437aec..15dcc1418c 100644 --- a/launch/jdt.ls.socket-stream.launch +++ b/launch/jdt.ls.socket-stream.launch @@ -40,12 +40,12 @@ - + - + diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/JavaClientConnection.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/JavaClientConnection.java index 22bff38b8f..6be5dbf446 100644 --- a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/JavaClientConnection.java +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/JavaClientConnection.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2016-2018 Red Hat Inc. and others. + * Copyright (c) 2016-2020 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 @@ -24,6 +24,7 @@ import org.eclipse.lsp4j.ApplyWorkspaceEditParams; import org.eclipse.lsp4j.ApplyWorkspaceEditResponse; import org.eclipse.lsp4j.Command; +import org.eclipse.lsp4j.ConfigurationParams; import org.eclipse.lsp4j.ExecuteCommandParams; import org.eclipse.lsp4j.MessageActionItem; import org.eclipse.lsp4j.MessageParams; @@ -222,6 +223,13 @@ public void semanticHighlighting(SemanticHighlightingParams params) { client.semanticHighlighting(params); } + /** + * @see {@link LanguageClient#configuration(ConfigurationParams)} + */ + public List configuration(ConfigurationParams configurationParams) { + return this.client.configuration(configurationParams).join(); + } + public void disconnect() { if (logHandler != null) { logHandler.uninstall(); diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/codemanipulation/GenerateGetterSetterOperation.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/codemanipulation/GenerateGetterSetterOperation.java index 31565a0779..b289e1cd4b 100644 --- a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/codemanipulation/GenerateGetterSetterOperation.java +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/codemanipulation/GenerateGetterSetterOperation.java @@ -15,6 +15,7 @@ import java.util.ArrayList; import java.util.List; +import java.util.Map; import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.CoreException; @@ -158,7 +159,8 @@ private void insertMethod(IField field, ListRewrite rewrite, AccessorKind kind) stub = GetterSetterUtil.getSetterStub(field, name, generateComments, flags); } - String formattedStub = CodeFormatterUtil.format(CodeFormatter.K_CLASS_BODY_DECLARATIONS, stub, 0, delimiter, type.getJavaProject().getOptions(true)); + Map options = type.getCompilationUnit() != null ? type.getCompilationUnit().getOptions(true) : type.getJavaProject().getOptions(true); + String formattedStub = CodeFormatterUtil.format(CodeFormatter.K_CLASS_BODY_DECLARATIONS, stub, 0, delimiter, options); MethodDeclaration declaration = (MethodDeclaration) rewrite.getASTRewrite().createStringPlaceholder(formattedStub, ASTNode.METHOD_DECLARATION); rewrite.insertLast(declaration, null); } diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/contentassist/AnonymousTypeCompletionProposal.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/contentassist/AnonymousTypeCompletionProposal.java index cc8fbc2cab..b478009658 100644 --- a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/contentassist/AnonymousTypeCompletionProposal.java +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/contentassist/AnonymousTypeCompletionProposal.java @@ -99,9 +99,8 @@ public String updateReplacementString(IDocument document, int offset, ImportRewr buf.append(newBody); // use the code formatter String lineDelim = TextUtilities.getDefaultLineDelimiter(document); - final IJavaProject project = fCompilationUnit.getJavaProject(); IRegion lineInfo = document.getLineInformationOfOffset(fReplacementOffset); - Map options = project != null ? project.getOptions(true) : JavaCore.getOptions(); + Map options = fCompilationUnit.getOptions(true); String replacementString = CodeFormatterUtil.format(CodeFormatter.K_EXPRESSION, buf.toString(), 0, lineDelim, options); int lineEndOffset = lineInfo.getOffset() + lineInfo.getLength(); int p = offset; diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/contentassist/OverrideCompletionProposal.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/contentassist/OverrideCompletionProposal.java index 4d85fcfcd9..3257b5eada 100644 --- a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/contentassist/OverrideCompletionProposal.java +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/contentassist/OverrideCompletionProposal.java @@ -147,7 +147,7 @@ public String updateReplacementString(IDocument document, int offset, ImportRewr ITrackedNodePosition position= rewrite.track(stub); try { - Map options = fJavaProject.getOptions(true); + Map options = fCompilationUnit.getOptions(true); rewrite.rewriteAST(recoveredDocument, options).apply(recoveredDocument); diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corext/refactoring/code/ExtractMethodRefactoring.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corext/refactoring/code/ExtractMethodRefactoring.java index eea50becfb..e497c0a8e5 100644 --- a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corext/refactoring/code/ExtractMethodRefactoring.java +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corext/refactoring/code/ExtractMethodRefactoring.java @@ -600,7 +600,7 @@ public Change createChange(IProgressMonitor pm) throws CoreException { result.addTextEditGroup(new TextEditGroup(RefactoringCoreMessages.ExtractMethodRefactoring_organize_imports, new TextEdit[] { edit })); } try { - Map formatter = this.fFormatterOptions == null ? fCUnit.getJavaProject().getOptions(true) : this.fFormatterOptions; + Map formatter = this.fFormatterOptions == null ? fCUnit.getOptions(true) : this.fFormatterOptions; IDocument document = new Document(fCUnit.getSource()); root.addChild(fRewriter.rewriteAST(document, formatter)); } catch (JavaModelException e) { diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corext/refactoring/delegates/DelegateCreator.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corext/refactoring/delegates/DelegateCreator.java index 660582313b..045877949d 100644 --- a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corext/refactoring/delegates/DelegateCreator.java +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corext/refactoring/delegates/DelegateCreator.java @@ -361,7 +361,7 @@ private void createJavadoc() throws JavaModelException { public void createEdit() throws JavaModelException { try { IDocument document= new Document(fDelegateRewrite.getCu().getBuffer().getContents()); - TextEdit edit= fDelegateRewrite.getASTRewrite().rewriteAST(document, fDelegateRewrite.getCu().getJavaProject().getOptions(true)); + TextEdit edit= fDelegateRewrite.getASTRewrite().rewriteAST(document, fDelegateRewrite.getCu().getOptions(true)); edit.apply(document, TextEdit.UPDATE_REGIONS); int tabWidth = CodeFormatterUtil.getTabWidth(fOriginalRewrite.getCu().getJavaProject()); diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corext/refactoring/reorg/DeleteChangeCreator.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corext/refactoring/reorg/DeleteChangeCreator.java index 594ad1149f..9b18bfeb08 100644 --- a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corext/refactoring/reorg/DeleteChangeCreator.java +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corext/refactoring/reorg/DeleteChangeCreator.java @@ -133,7 +133,7 @@ private static Change createDeleteChange(ICompilationUnit cu, List private static TextChange addTextEditFromRewrite(TextChangeManager manager, ICompilationUnit cu, ASTRewrite rewrite) throws CoreException { try { ITextFileBuffer buffer= RefactoringFileBuffers.acquire(cu); - TextEdit resultingEdits= rewrite.rewriteAST(buffer.getDocument(), cu.getJavaProject().getOptions(true)); + TextEdit resultingEdits= rewrite.rewriteAST(buffer.getDocument(), cu.getOptions(true)); TextChange textChange= manager.get(cu); if (textChange instanceof TextFileChange) { TextFileChange tfc= (TextFileChange) textChange; diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corrections/proposals/AbstractMethodCorrectionProposal.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corrections/proposals/AbstractMethodCorrectionProposal.java index 41463f4843..617d246cc9 100644 --- a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corrections/proposals/AbstractMethodCorrectionProposal.java +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corrections/proposals/AbstractMethodCorrectionProposal.java @@ -154,7 +154,7 @@ private MethodDeclaration getStub(ASTRewrite rewrite, ASTNode targetTypeDecl) th if (!isAbstractMethod && !isVoid) { ReturnStatement returnStatement= ast.newReturnStatement(); returnStatement.setExpression(ASTNodeFactory.newDefaultExpression(ast, returnType, 0)); - bodyStatement= ASTNodes.asFormattedString(returnStatement, 0, String.valueOf('\n'), getCompilationUnit().getJavaProject().getOptions(true)); + bodyStatement= ASTNodes.asFormattedString(returnStatement, 0, String.valueOf('\n'), getCompilationUnit().getOptions(true)); } } diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corrections/proposals/ConstructorFromSuperclassProposal.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corrections/proposals/ConstructorFromSuperclassProposal.java index 2c703ba2f8..2f23682401 100644 --- a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corrections/proposals/ConstructorFromSuperclassProposal.java +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corrections/proposals/ConstructorFromSuperclassProposal.java @@ -163,7 +163,7 @@ private MethodDeclaration createNewMethodDeclaration(AST ast, IMethodBinding bin } } - String bodyStatement = (invocation == null) ? "" : ASTNodes.asFormattedString(invocation, 0, String.valueOf('\n'), getCompilationUnit().getJavaProject().getOptions(true)); //$NON-NLS-1$ + String bodyStatement = (invocation == null) ? "" : ASTNodes.asFormattedString(invocation, 0, String.valueOf('\n'), getCompilationUnit().getOptions(true)); //$NON-NLS-1$ String placeHolder= CodeGeneration.getMethodBodyContent(getCompilationUnit(), name, name, true, bodyStatement, String.valueOf('\n')); if (placeHolder != null) { ASTNode todoNode= rewrite.createStringPlaceholder(placeHolder, ASTNode.RETURN_STATEMENT); diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corrections/proposals/ModifierChangeCorrectionProposal.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corrections/proposals/ModifierChangeCorrectionProposal.java index 304045e268..9d344d2b74 100644 --- a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corrections/proposals/ModifierChangeCorrectionProposal.java +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corrections/proposals/ModifierChangeCorrectionProposal.java @@ -113,7 +113,7 @@ protected ASTRewrite getRewrite() throws CoreException { if (expression != null) { ReturnStatement returnStatement = ast.newReturnStatement(); returnStatement.setExpression(expression); - bodyStatement = ASTNodes.asFormattedString(returnStatement, 0, delimiter, unit.getJavaProject().getOptions(true)); + bodyStatement = ASTNodes.asFormattedString(returnStatement, 0, delimiter, unit.getOptions(true)); } } String placeHolder = CodeGeneration.getMethodBodyContent(unit, methodBinding.getDeclaringClass().getName(), methodBinding.getName(), false, bodyStatement, delimiter); diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/CodeActionHandler.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/CodeActionHandler.java index cc3b49e96c..0a757a8803 100644 --- a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/CodeActionHandler.java +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/CodeActionHandler.java @@ -16,7 +16,9 @@ import java.util.Arrays; import java.util.Collections; import java.util.Comparator; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.stream.Collectors; @@ -26,7 +28,9 @@ import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.jdt.core.ICompilationUnit; import org.eclipse.jdt.core.IJavaModelMarker; +import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.dom.CompilationUnit; +import org.eclipse.jdt.core.formatter.DefaultCodeFormatterConstants; import org.eclipse.jdt.core.manipulation.CoreASTProvider; import org.eclipse.jdt.internal.ui.text.correction.IProblemLocationCore; import org.eclipse.jdt.internal.ui.text.correction.ProblemLocationCore; @@ -40,6 +44,7 @@ import org.eclipse.jdt.ls.core.internal.corrections.RefactorProcessor; import org.eclipse.jdt.ls.core.internal.corrections.proposals.ChangeCorrectionProposal; import org.eclipse.jdt.ls.core.internal.preferences.PreferenceManager; +import org.eclipse.jdt.ls.core.internal.preferences.Preferences; import org.eclipse.jdt.ls.core.internal.text.correction.AssignToVariableAssistCommandProposal; import org.eclipse.jdt.ls.core.internal.text.correction.CUCorrectionCommandProposal; import org.eclipse.jdt.ls.core.internal.text.correction.NonProjectFixProcessor; @@ -86,6 +91,32 @@ public List> getCodeActionCommands(CodeActionParams return Collections.emptyList(); } + Map formattingOptions = ConfigurationHandler.getFormattingOptions(params.getTextDocument().getUri()); + if (formattingOptions != null && !formattingOptions.isEmpty()) { + Object tabSizeValue = formattingOptions.get(Preferences.JAVA_CONFIGURATION_TABSIZE); + Object insertSpacesValue = formattingOptions.get(Preferences.JAVA_CONFIGURATION_INSERTSPACES); + Map customOptions = new HashMap<>(); + if (tabSizeValue != null) { + try { + int tabSize = Integer.parseInt(String.valueOf(tabSizeValue)); + if (tabSize > 0) { + customOptions.put(DefaultCodeFormatterConstants.FORMATTER_TAB_SIZE, Integer.toString(tabSize)); + } + } catch (Exception ex) { + // do nothing + } + } + + if (insertSpacesValue != null) { + boolean insertSpaces = Boolean.parseBoolean(String.valueOf(insertSpacesValue)); + customOptions.put(DefaultCodeFormatterConstants.FORMATTER_TAB_CHAR, insertSpaces ? JavaCore.SPACE : JavaCore.TAB); + } + + if (!customOptions.isEmpty()) { + unit.setOptions(customOptions); + } + } + CompilationUnit astRoot = getASTRoot(unit, monitor); if (astRoot == null || monitor.isCanceled()) { return Collections.emptyList(); diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/ConfigurationHandler.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/ConfigurationHandler.java new file mode 100644 index 0000000000..0f26538551 --- /dev/null +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/ConfigurationHandler.java @@ -0,0 +1,65 @@ +/******************************************************************************* + * Copyright (c) 2020 Microsoft Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Microsoft Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.jdt.ls.core.internal.handlers; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.eclipse.jdt.ls.core.internal.JavaLanguageServerPlugin; +import org.eclipse.jdt.ls.core.internal.preferences.Preferences; +import org.eclipse.lsp4j.ConfigurationItem; +import org.eclipse.lsp4j.ConfigurationParams; + +public class ConfigurationHandler { + + private ConfigurationHandler() {} + + /** + * Query the format setting (insertSpace and tabSize) for the given uri. + * Will return {@code null} if the 'workspace/configuration' request is not supported, + * @param uri The target uri to query + * @return a map stores the setting keys and their values, for any setting key which is not + * available at the client side, a {@code null} value will be provided. + */ + public static Map getFormattingOptions(String uri) { + if (!JavaLanguageServerPlugin.getPreferencesManager().getClientPreferences().isWorkspaceConfigurationSupported()) { + return null; + }; + + List configurationItems = new ArrayList<>(); + String[] settingKeys = { + Preferences.JAVA_CONFIGURATION_TABSIZE, + Preferences.JAVA_CONFIGURATION_INSERTSPACES + }; + + ConfigurationItem tabSizeItem = new ConfigurationItem(); + tabSizeItem.setScopeUri(uri); + tabSizeItem.setSection(settingKeys[0]); + configurationItems.add(tabSizeItem); + + ConfigurationItem insertSpacesItem = new ConfigurationItem(); + insertSpacesItem.setScopeUri(uri); + insertSpacesItem.setSection(settingKeys[1]); + configurationItems.add(insertSpacesItem); + + ConfigurationParams configurationParams = new ConfigurationParams(configurationItems); + List response = JavaLanguageServerPlugin.getInstance().getClientConnection().configuration(configurationParams); + + Map results = new HashMap<>(); + int minLength = Math.min(settingKeys.length, response.size()); + for (int i = 0; i < minLength; i++) { + results.put(settingKeys[i], response.get(i)); + } + return results; + } +} 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 99b1946415..906699fa51 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 @@ -141,7 +141,7 @@ private IRegion getRegion(Range range, IDocument document) { } public static Map getOptions(FormattingOptions options, ICompilationUnit cu) { - Map eclipseOptions = cu.getJavaProject().getOptions(true); + Map eclipseOptions = cu.getOptions(true); Map customOptions = options.entrySet().stream().filter(map -> chekIfValueIsNotNull(map.getValue())).collect(toMap(e -> e.getKey(), e -> getOptionValue(e.getValue()))); diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/JDTLanguageServer.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/JDTLanguageServer.java index ffab801d57..7999c7eacc 100644 --- a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/JDTLanguageServer.java +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/JDTLanguageServer.java @@ -41,6 +41,7 @@ import org.eclipse.jdt.ls.core.internal.JDTUtils; import org.eclipse.jdt.ls.core.internal.JSONUtility; import org.eclipse.jdt.ls.core.internal.JVMConfigurator; +import org.eclipse.jdt.ls.core.internal.JavaClientConnection; import org.eclipse.jdt.ls.core.internal.JavaClientConnection.JavaLanguageClient; import org.eclipse.jdt.ls.core.internal.JavaLanguageServerPlugin; import org.eclipse.jdt.ls.core.internal.JobHelpers; @@ -214,6 +215,11 @@ public void connectClient(JavaLanguageClient client) { this.documentLifeCycleHandler = new DocumentLifeCycleHandler(this.client, preferenceManager, pm, true); } + // For testing purpose + public void setClientConnection(JavaClientConnection client) { + this.client = client; + } + //For testing purposes public void disconnectClient() { Job.getJobManager().setProgressProvider(null); diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/WorkspaceSymbolHandler.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/WorkspaceSymbolHandler.java index 32b8ebb202..b2b54faa9b 100644 --- a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/WorkspaceSymbolHandler.java +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/WorkspaceSymbolHandler.java @@ -64,6 +64,9 @@ public static List search(String query, int maxResults, Strin @Override public void acceptTypeNameMatch(TypeNameMatch match) { try { + if (maxResults > 0 && symbols.size() >= maxResults) { + return; + } Location location = null; try { if (!sourceOnly && match.getType().isBinary()) { diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/preferences/ClientPreferences.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/preferences/ClientPreferences.java index faed1d3193..38d1c4f56a 100644 --- a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/preferences/ClientPreferences.java +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/preferences/ClientPreferences.java @@ -119,6 +119,10 @@ public boolean isWorkspaceChangeWatchedFilesDynamicRegistered() { return v3supported && capabilities.getWorkspace() != null && isDynamicRegistrationSupported(capabilities.getWorkspace().getDidChangeWatchedFiles()); } + public boolean isWorkspaceConfigurationSupported() { + return v3supported && capabilities.getWorkspace() != null && isTrue(capabilities.getWorkspace().getConfiguration()); + } + public boolean isDocumentSymbolDynamicRegistered() { return v3supported && isDynamicRegistrationSupported(capabilities.getTextDocument().getDocumentSymbol()); } 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 bbec4ec4da..574635b56a 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 @@ -68,6 +68,14 @@ public class Preferences { * references. */ public static final String JAVA_REFERENCES_INCLUDE_DECOMPILED_SOURCES = "java.references.includeDecompiledSources"; + /** + * Insert spaces when pressing Tab + */ + public static final String JAVA_CONFIGURATION_INSERTSPACES = "java.format.insertSpaces"; + /** + * Tab Size + */ + public static final String JAVA_CONFIGURATION_TABSIZE = "java.format.tabSize"; /** * Specifies Java Execution Environments. */ diff --git a/org.eclipse.jdt.ls.target/org.eclipse.jdt.ls.tp.target b/org.eclipse.jdt.ls.target/org.eclipse.jdt.ls.tp.target index c44e21edd9..15f82a831b 100644 --- a/org.eclipse.jdt.ls.target/org.eclipse.jdt.ls.tp.target +++ b/org.eclipse.jdt.ls.target/org.eclipse.jdt.ls.tp.target @@ -31,6 +31,9 @@ + + + diff --git a/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/correction/AbstractQuickFixTest.java b/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/correction/AbstractQuickFixTest.java index 0cdd9964aa..38a75760ea 100644 --- a/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/correction/AbstractQuickFixTest.java +++ b/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/correction/AbstractQuickFixTest.java @@ -302,13 +302,17 @@ protected String evaluateCodeActionCommand(Either codeActio Assert.assertNotNull(c.getArguments()); Assert.assertTrue(c.getArguments().get(0) instanceof WorkspaceEdit); WorkspaceEdit we = (WorkspaceEdit) c.getArguments().get(0); - if (we.getDocumentChanges() != null) { - return ResourceUtils.dos2Unix(evaluateChanges(we.getDocumentChanges())); + return evaluateWorkspaceEdit(we); + } + + public static String evaluateWorkspaceEdit(WorkspaceEdit edit) throws JavaModelException, BadLocationException { + if (edit.getDocumentChanges() != null) { + return ResourceUtils.dos2Unix(evaluateChanges(edit.getDocumentChanges())); } - return ResourceUtils.dos2Unix(evaluateChanges(we.getChanges())); + return ResourceUtils.dos2Unix(evaluateChanges(edit.getChanges())); } - private String evaluateChanges(List> documentChanges) throws BadLocationException, JavaModelException { + public static String evaluateChanges(List> documentChanges) throws BadLocationException, JavaModelException { List changes = documentChanges.stream().filter(e -> e.isLeft()).map(e -> e.getLeft()).collect(Collectors.toList()); assertFalse("No edits generated", changes.isEmpty()); Set uris = changes.stream().map(tde -> tde.getTextDocument().getUri()).distinct().collect(Collectors.toSet()); @@ -318,7 +322,7 @@ private String evaluateChanges(List> return ResourceUtils.dos2Unix(evaluateChanges(uri, edits)); } - protected String evaluateChanges(Map> changes) throws BadLocationException, JavaModelException { + public static String evaluateChanges(Map> changes) throws BadLocationException, JavaModelException { Iterator>> editEntries = changes.entrySet().iterator(); Entry> entry = editEntries.next(); assertNotNull("No edits generated", entry); @@ -326,7 +330,7 @@ protected String evaluateChanges(Map> changes) throws Bad return ResourceUtils.dos2Unix(evaluateChanges(entry.getKey(), entry.getValue())); } - private String evaluateChanges(String uri, List edits) throws BadLocationException, JavaModelException { + private static String evaluateChanges(String uri, List edits) throws BadLocationException, JavaModelException { assertFalse("No edits generated: " + edits, edits == null || edits.isEmpty()); ICompilationUnit cu = JDTUtils.resolveCompilationUnit(uri); assertNotNull("CU not found: " + uri, cu); diff --git a/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/handlers/CodeActionHandlerTest.java b/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/handlers/CodeActionHandlerTest.java index 6813cec8f8..a00eddf7d6 100644 --- a/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/handlers/CodeActionHandlerTest.java +++ b/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/handlers/CodeActionHandlerTest.java @@ -14,27 +14,38 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.when; import java.net.URI; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.stream.Collectors; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.jdt.core.ICompilationUnit; +import org.eclipse.jdt.core.IJavaProject; +import org.eclipse.jdt.core.IPackageFragment; +import org.eclipse.jdt.core.IPackageFragmentRoot; +import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.compiler.IProblem; +import org.eclipse.jdt.core.formatter.DefaultCodeFormatterConstants; import org.eclipse.jdt.ls.core.internal.CodeActionUtil; import org.eclipse.jdt.ls.core.internal.JDTUtils; import org.eclipse.jdt.ls.core.internal.JavaClientConnection; import org.eclipse.jdt.ls.core.internal.JavaCodeActionKind; import org.eclipse.jdt.ls.core.internal.JavaLanguageServerPlugin; import org.eclipse.jdt.ls.core.internal.LanguageServerWorkingCopyOwner; +import org.eclipse.jdt.ls.core.internal.ProjectUtils; import org.eclipse.jdt.ls.core.internal.WorkspaceHelper; +import org.eclipse.jdt.ls.core.internal.codemanipulation.AbstractSourceTestCase; +import org.eclipse.jdt.ls.core.internal.correction.AbstractQuickFixTest; +import org.eclipse.jdt.ls.core.internal.preferences.ClientPreferences; import org.eclipse.jdt.ls.core.internal.preferences.PreferenceManager; import org.eclipse.lsp4j.CodeAction; import org.eclipse.lsp4j.CodeActionContext; @@ -54,6 +65,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; +import org.mockito.Mockito; import org.mockito.runners.MockitoJUnitRunner; /** @@ -65,7 +77,7 @@ public class CodeActionHandlerTest extends AbstractCompilationUnitBasedTest { @Mock private JavaClientConnection connection; - + private ClientPreferences clientPreferences; @Override @Before @@ -76,6 +88,12 @@ public void setup() throws Exception{ server = new JDTLanguageServer(projectsManager, this.preferenceManager); } + @Override + protected ClientPreferences initPreferenceManager(boolean supportClassFileContents) { + clientPreferences = super.initPreferenceManager(supportClassFileContents); + return clientPreferences; + } + @Test public void testCodeAction_removeUnusedImport() throws Exception{ ICompilationUnit unit = getWorkingCopy( @@ -440,6 +458,73 @@ public void testCodeAction_ignoringOtherDiagnosticWithoutCode() throws Exception Assert.assertEquals(CodeActionHandler.COMMAND_ID_APPLY_EDIT, c.getCommand()); } + @Test + public void testCodeAction_customFileFormattingOptions() throws Exception { + when(clientPreferences.isWorkspaceConfigurationSupported()).thenReturn(true); + when(connection.configuration(Mockito.any())).thenReturn(Arrays.asList(4, true/*Indent using Spaces*/)); + server.setClientConnection(connection); + JavaLanguageServerPlugin.getInstance().setProtocol(server); + IJavaProject javaProject = ProjectUtils.getJavaProject(project); + Map projectOptions = javaProject.getOptions(false); + projectOptions.put(DefaultCodeFormatterConstants.FORMATTER_TAB_CHAR, JavaCore.TAB); // Indent using Tabs + projectOptions.put(DefaultCodeFormatterConstants.FORMATTER_TAB_SIZE, "4"); + javaProject.setOptions(projectOptions); + + IPackageFragmentRoot sourceFolder = javaProject.getPackageFragmentRoot(project.getFolder("src")); + IPackageFragment pack1 = sourceFolder.createPackageFragment("test1", false, null); + StringBuilder builder = new StringBuilder(); + builder.append("package test1;\n"); + builder.append("interface I {\n"); + builder.append(" void method();\n"); + builder.append("}\n"); + builder.append("public class E {\n"); + builder.append(" void bar(I i) {\n"); + builder.append(" }\n"); + builder.append(" void foo() {\n"); + builder.append(" bar(() /*[*//*]*/-> {\n"); + builder.append(" });\n"); + builder.append(" }\n"); + builder.append("}\n"); + ICompilationUnit cu = pack1.createCompilationUnit("E.java", builder.toString(), false, null); + + CodeActionParams params = new CodeActionParams(); + params.setTextDocument(new TextDocumentIdentifier(JDTUtils.toURI(cu))); + final Range range = CodeActionUtil.getRange(cu, "/*[*//*]*/"); + params.setRange(range); + params.setContext(new CodeActionContext(Collections.emptyList(), Arrays.asList(CodeActionKind.Refactor))); + List> codeActions = getCodeActions(params); + Assert.assertNotNull(codeActions); + Optional> found = codeActions.stream().filter((codeAction) -> { + return codeAction.isRight() && Objects.equals("Convert to anonymous class creation", codeAction.getRight().getTitle()); + }).findAny(); + Assert.assertTrue(found.isPresent()); + + Either codeAction = found.get(); + Command c = codeAction.isLeft() ? codeAction.getLeft() : codeAction.getRight().getCommand(); + Assert.assertEquals(CodeActionHandler.COMMAND_ID_APPLY_EDIT, c.getCommand()); + Assert.assertNotNull(c.getArguments()); + Assert.assertTrue(c.getArguments().get(0) instanceof WorkspaceEdit); + WorkspaceEdit edit = (WorkspaceEdit) c.getArguments().get(0); + String actual = AbstractQuickFixTest.evaluateWorkspaceEdit(edit); + builder = new StringBuilder(); + builder.append("package test1;\n"); + builder.append("interface I {\n"); + builder.append(" void method();\n"); + builder.append("}\n"); + builder.append("public class E {\n"); + builder.append(" void bar(I i) {\n"); + builder.append(" }\n"); + builder.append(" void foo() {\n"); + builder.append(" bar(new I() {\n"); + builder.append(" @Override\n"); + builder.append(" public void method() {\n"); + builder.append(" }\n"); + builder.append(" });\n"); + builder.append(" }\n"); + builder.append("}\n"); + AbstractSourceTestCase.compareSource(builder.toString(), actual); + } + private List> getCodeActions(CodeActionParams params) { return server.codeAction(params).join(); } diff --git a/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/handlers/ConfigurationHandlerTest.java b/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/handlers/ConfigurationHandlerTest.java new file mode 100644 index 0000000000..544d54d3fd --- /dev/null +++ b/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/handlers/ConfigurationHandlerTest.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2020 Microsoft Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Microsoft Corporation - initial API and implementation + *******************************************************************************/ + +package org.eclipse.jdt.ls.core.internal.handlers; + +import static org.junit.Assert.assertNull; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import org.eclipse.jdt.ls.core.internal.managers.AbstractProjectsManagerBasedTest; +import org.eclipse.jdt.ls.core.internal.preferences.ClientPreferences; +import org.junit.Test; + +public class ConfigurationHandlerTest extends AbstractProjectsManagerBasedTest { + + @Test + public void testGetConfigurationWhenItIsNotSupported() { + ClientPreferences clientPreferences = mock(ClientPreferences.class); + when(clientPreferences.isWorkspaceConfigurationSupported()).thenReturn(false); + when(preferenceManager.getClientPreferences()).thenReturn(clientPreferences); + + assertNull(ConfigurationHandler.getFormattingOptions("fakeUri")); + } +}