You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@netbeans.apache.org by db...@apache.org on 2021/09/13 07:37:22 UTC
[netbeans] branch master updated: LSP: Surround With refactorings
implemented. (#3157)
This is an automated email from the ASF dual-hosted git repository.
dbalek pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/netbeans.git
The following commit(s) were added to refs/heads/master by this push:
new 58dade6 LSP: Surround With refactorings implemented. (#3157)
58dade6 is described below
commit 58dade642b6c3a123172d1dd966fc13b32b83d35
Author: Dusan Balek <du...@oracle.com>
AuthorDate: Mon Sep 13 09:37:03 2021 +0200
LSP: Surround With refactorings implemented. (#3157)
* LSP: Surround With refactorings implemented.
* LSP: Code templates added to code completion.
---
.../codetemplates/GsfCodeTemplateFilter.java | 14 +-
ide/editor.codetemplates/apichanges.xml | 12 +
.../nbproject/project.properties | 2 +-
ide/editor.codetemplates/nbproject/project.xml | 25 +-
.../lib/editor/codetemplates/AbbrevDetection.java | 2 +-
.../CodeTemplateCompletionProvider.java | 149 +++++++++-
.../CodeTemplateManagerOperation.java | 6 +-
.../lib/editor/codetemplates/SurroundWithFix.java | 2 +-
.../codetemplates/spi/CodeTemplateFilter.java | 18 ++
.../editor/java/JavaCodeTemplateFilter.java | 16 +-
.../netbeans/modules/editor/java/Utilities.java | 5 +-
.../java/editor/resources/DefaultAbbrevs.xml | 94 +++---
java/java.lsp.server/nbproject/project.xml | 9 +
.../ExtractSuperclassOrInterfaceRefactoring.java | 10 +-
.../java/lsp/server/protocol/MoveRefactoring.java | 8 +-
.../lsp/server/protocol/PullUpRefactoring.java | 10 +-
.../lsp/server/protocol/PushDownRefactoring.java | 15 +-
.../java/lsp/server/protocol/SurroundWithHint.java | 329 +++++++++++++++++++++
.../java/lsp/server/protocol/ServerTest.java | 79 +++++
19 files changed, 709 insertions(+), 96 deletions(-)
diff --git a/ide/csl.api/src/org/netbeans/modules/csl/editor/codetemplates/GsfCodeTemplateFilter.java b/ide/csl.api/src/org/netbeans/modules/csl/editor/codetemplates/GsfCodeTemplateFilter.java
index 8ddd4b2..ef98297 100644
--- a/ide/csl.api/src/org/netbeans/modules/csl/editor/codetemplates/GsfCodeTemplateFilter.java
+++ b/ide/csl.api/src/org/netbeans/modules/csl/editor/codetemplates/GsfCodeTemplateFilter.java
@@ -40,10 +40,9 @@ public class GsfCodeTemplateFilter implements CodeTemplateFilter {
private int endOffset;
private Set<String> templates;
- private GsfCodeTemplateFilter(JTextComponent component, int offset) {
- this.startOffset = offset;
- this.endOffset = component.getSelectionStart() == offset ? component.getSelectionEnd() : -1;
- Document doc = component.getDocument();
+ private GsfCodeTemplateFilter(Document doc, int startOffset, int endOffset) {
+ this.startOffset = startOffset;
+ this.endOffset = endOffset;
CodeCompletionHandler completer = doc == null ? null : GsfCompletionProvider.getCompletable(doc, startOffset);
if (completer != null) {
@@ -68,7 +67,12 @@ public class GsfCodeTemplateFilter implements CodeTemplateFilter {
@Override
public CodeTemplateFilter createFilter(JTextComponent component, int offset) {
- return new GsfCodeTemplateFilter(component, offset);
+ return createFilter(component.getDocument(), offset, component.getSelectionStart() == offset ? component.getSelectionEnd() : -1);
+ }
+
+ @Override
+ public CodeTemplateFilter createFilter(Document doc, int startOffset, int endOffset) {
+ return new GsfCodeTemplateFilter(doc, startOffset, endOffset);
}
}
diff --git a/ide/editor.codetemplates/apichanges.xml b/ide/editor.codetemplates/apichanges.xml
index 54177d6..63b6291 100644
--- a/ide/editor.codetemplates/apichanges.xml
+++ b/ide/editor.codetemplates/apichanges.xml
@@ -84,6 +84,18 @@ is the proper place.
<!-- ACTUAL CHANGES BEGIN HERE: -->
<changes>
+ <change id="CodeTemplateFilter.Factory.createForDoc">
+ <api name="codetemplates"/>
+ <summary>Default method <code>createFilter(Document, int, int)</code> added to <code>CodeTemplateFilter.Factory</code>.</summary>
+ <version major="1" minor="57"/>
+ <date day="9" month="9" year="2021"/>
+ <author login="dbalek"/>
+ <compatibility addition="yes" binary="compatible" deletion="no" deprecation="no" modification="no" semantic="compatible" source="compatible"/>
+ <description>
+ Added method allows creating code template filters for the given document and range.
+ </description>
+ <class package="org.netbeans.lib.editor.codetemplates.spi" name="CodeTemplateFilter"/>
+ </change>
<change id="orderingAttribute.added">
<api name="codetemplates"/>
<summary>Added <code>ordering</code> for code template parameters.</summary>
diff --git a/ide/editor.codetemplates/nbproject/project.properties b/ide/editor.codetemplates/nbproject/project.properties
index 561dc23..213ea65 100644
--- a/ide/editor.codetemplates/nbproject/project.properties
+++ b/ide/editor.codetemplates/nbproject/project.properties
@@ -20,6 +20,6 @@ javac.source=1.8
#javadoc.name=EditorCodeTemplates
javadoc.apichanges=${basedir}/apichanges.xml
javadoc.arch=${basedir}/arch.xml
-spec.version.base=1.56.0
+spec.version.base=1.57.0
test.config.stableBTD.includes=**/*Test.class
diff --git a/ide/editor.codetemplates/nbproject/project.xml b/ide/editor.codetemplates/nbproject/project.xml
index 1714265..1e05a0c 100644
--- a/ide/editor.codetemplates/nbproject/project.xml
+++ b/ide/editor.codetemplates/nbproject/project.xml
@@ -26,6 +26,15 @@
<code-name-base>org.netbeans.modules.editor.codetemplates</code-name-base>
<module-dependencies>
<dependency>
+ <code-name-base>org.netbeans.api.lsp</code-name-base>
+ <build-prerequisite/>
+ <compile-dependency/>
+ <run-dependency>
+ <release-version>1</release-version>
+ <specification-version>1.5</specification-version>
+ </run-dependency>
+ </dependency>
+ <dependency>
<code-name-base>org.netbeans.modules.editor</code-name-base>
<build-prerequisite/>
<compile-dependency/>
@@ -96,19 +105,19 @@
</run-dependency>
</dependency>
<dependency>
- <code-name-base>org.netbeans.modules.editor.settings.storage</code-name-base>
+ <code-name-base>org.netbeans.modules.editor.settings.lib</code-name-base>
<build-prerequisite/>
<compile-dependency/>
<run-dependency>
- <release-version>1</release-version>
<specification-version>1.49</specification-version>
</run-dependency>
</dependency>
<dependency>
- <code-name-base>org.netbeans.modules.editor.settings.lib</code-name-base>
+ <code-name-base>org.netbeans.modules.editor.settings.storage</code-name-base>
<build-prerequisite/>
<compile-dependency/>
<run-dependency>
+ <release-version>1</release-version>
<specification-version>1.49</specification-version>
</run-dependency>
</dependency>
@@ -189,7 +198,7 @@
</run-dependency>
</dependency>
<dependency>
- <code-name-base>org.openide.util.ui</code-name-base>
+ <code-name-base>org.openide.util</code-name-base>
<build-prerequisite/>
<compile-dependency/>
<run-dependency>
@@ -197,19 +206,19 @@
</run-dependency>
</dependency>
<dependency>
- <code-name-base>org.openide.util</code-name-base>
+ <code-name-base>org.openide.util.lookup</code-name-base>
<build-prerequisite/>
<compile-dependency/>
<run-dependency>
- <specification-version>9.3</specification-version>
+ <specification-version>8.0</specification-version>
</run-dependency>
</dependency>
<dependency>
- <code-name-base>org.openide.util.lookup</code-name-base>
+ <code-name-base>org.openide.util.ui</code-name-base>
<build-prerequisite/>
<compile-dependency/>
<run-dependency>
- <specification-version>8.0</specification-version>
+ <specification-version>9.3</specification-version>
</run-dependency>
</dependency>
<dependency>
diff --git a/ide/editor.codetemplates/src/org/netbeans/lib/editor/codetemplates/AbbrevDetection.java b/ide/editor.codetemplates/src/org/netbeans/lib/editor/codetemplates/AbbrevDetection.java
index 8281dc1..d121d22 100644
--- a/ide/editor.codetemplates/src/org/netbeans/lib/editor/codetemplates/AbbrevDetection.java
+++ b/ide/editor.codetemplates/src/org/netbeans/lib/editor/codetemplates/AbbrevDetection.java
@@ -508,7 +508,7 @@ public final class AbbrevDetection implements DocumentListener, PropertyChangeLi
op.waitLoaded();
CodeTemplate ct = op.findByAbbreviation(abbrev.toString());
if (ct != null) {
- if (accept(ct, CodeTemplateManagerOperation.getTemplateFilters(component, abbrevStartOffset))) {
+ if (accept(ct, CodeTemplateManagerOperation.getTemplateFilters(component.getDocument(), abbrevStartOffset, abbrevStartOffset))) {
Document doc = component.getDocument();
sendUndoableEdit(doc, CloneableEditorSupport.BEGIN_COMMIT_GROUP);
try {
diff --git a/ide/editor.codetemplates/src/org/netbeans/lib/editor/codetemplates/CodeTemplateCompletionProvider.java b/ide/editor.codetemplates/src/org/netbeans/lib/editor/codetemplates/CodeTemplateCompletionProvider.java
index b9617af..e0ec7e9 100644
--- a/ide/editor.codetemplates/src/org/netbeans/lib/editor/codetemplates/CodeTemplateCompletionProvider.java
+++ b/ide/editor.codetemplates/src/org/netbeans/lib/editor/codetemplates/CodeTemplateCompletionProvider.java
@@ -19,29 +19,42 @@
package org.netbeans.lib.editor.codetemplates;
+import java.io.IOException;
+import java.io.StringReader;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
+import java.util.Map;
import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
import java.util.prefs.Preferences;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.text.AbstractDocument;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.JTextComponent;
+import javax.swing.text.html.HTMLEditorKit;
+import javax.swing.text.html.parser.ParserDelegator;
import org.netbeans.api.editor.mimelookup.MimeLookup;
import org.netbeans.api.editor.mimelookup.MimePath;
import org.netbeans.api.editor.mimelookup.MimeRegistration;
import org.netbeans.api.editor.settings.SimpleValueNames;
import org.netbeans.api.lexer.TokenHierarchy;
import org.netbeans.api.lexer.TokenSequence;
+import org.netbeans.api.lsp.Completion;
import org.netbeans.editor.BaseDocument;
import org.netbeans.editor.Utilities;
import org.netbeans.lib.editor.codetemplates.api.CodeTemplate;
import org.netbeans.lib.editor.codetemplates.spi.CodeTemplateFilter;
+import org.netbeans.lib.editor.codetemplates.spi.CodeTemplateParameter;
import org.netbeans.lib.editor.util.swing.DocumentUtilities;
import org.netbeans.modules.editor.NbEditorUtilities;
import org.netbeans.spi.editor.completion.CompletionItem;
@@ -50,6 +63,7 @@ import org.netbeans.spi.editor.completion.CompletionResultSet;
import org.netbeans.spi.editor.completion.CompletionTask;
import org.netbeans.spi.editor.completion.support.AsyncCompletionQuery;
import org.netbeans.spi.editor.completion.support.AsyncCompletionTask;
+import org.netbeans.spi.lsp.CompletionCollector;
/**
* Implemenation of the code template description.
@@ -70,7 +84,115 @@ public final class CodeTemplateCompletionProvider implements CompletionProvider
private static boolean isAbbrevDisabled(JTextComponent component) {
return org.netbeans.editor.Abbrev.isAbbrevDisabled(component);
}
-
+
+ @MimeRegistration(mimeType = "text/x-java", service = CompletionCollector.class)
+ public static class Collector implements CompletionCollector {
+
+ private static final String SELECTED_TEXT_VAR = "${0:$TM_SELECTED_TEXT}";
+ private static final Pattern SNIPPET_VAR_PATTERN = Pattern.compile("\\$\\{\\s*([-\\w]++)([^}]*)?}");
+ private static final Pattern SNIPPET_HINT_PATTERN = Pattern.compile("([-\\w]++)(?:\\s*=\\s*(\\\"([^\\\"]*)\\\"|\\S*))?");
+
+ @Override
+ public boolean collectCompletions(Document doc, int offset, Completion.Context context, Consumer<Completion> consumer) {
+ new Query().query(doc, offset, (ct, abbrevBased) -> {
+ String description = ct.getDescription();
+ if (description == null) {
+ description = CodeTemplateApiPackageAccessor.get().getSingleLineText(ct);
+ }
+ String label = html2text(description.trim());
+ consumer.accept(CompletionCollector.newBuilder(label)
+ .sortText(String.format("%04d%s", 1650, label))
+ .documentation(() -> {
+ StringBuffer sb = new StringBuffer("<html><pre>");
+ ParametrizedTextParser.parseToHtml(sb, ct.getParametrizedText());
+ sb.append("</pre>");
+ return sb.toString();
+ }).insertText(convert(ct.getParametrizedText()))
+ .insertTextFormat(Completion.TextFormat.Snippet)
+ .build());
+ });
+ return true;
+ }
+
+ private static String convert(String s) {
+ StringBuilder sb = new StringBuilder();
+ Matcher matcher = SNIPPET_VAR_PATTERN.matcher(s);
+ int idx = 0;
+ Map<String, Integer> placeholders = new HashMap<>();
+ Map<String, String> values = new HashMap<>();
+ Set<String> nonEditables = new HashSet<>();
+ AtomicInteger last = new AtomicInteger();
+ while (matcher.find(idx)) {
+ int start = matcher.start();
+ sb.append(s.substring(idx, start));
+ String name = matcher.group(1);
+ switch (name) {
+ case CodeTemplateParameter.CURSOR_PARAMETER_NAME:
+ case CodeTemplateParameter.NO_FORMAT_PARAMETER_NAME:
+ case CodeTemplateParameter.NO_INDENT_PARAMETER_NAME:
+ break;
+ case CodeTemplateParameter.SELECTION_PARAMETER_NAME:
+ sb.append(SELECTED_TEXT_VAR);
+ break;
+ default:
+ String params = matcher.groupCount() > 1 ? matcher.group(2) : null;
+ if (params == null || params.isEmpty()) {
+ if (nonEditables.contains(name)) {
+ sb.append(values.getOrDefault(name, ""));
+ } else {
+ sb.append('$').append(placeholders.computeIfAbsent(name, n -> last.incrementAndGet()));
+ }
+ } else {
+ Map<String, String> hints = getHints(params);
+ String defaultValue = hints.get(CodeTemplateParameter.DEFAULT_VALUE_HINT_NAME);
+ if (defaultValue != null) {
+ values.put(name, defaultValue);
+ }
+ if ("false".equalsIgnoreCase(hints.get(CodeTemplateParameter.EDITABLE_HINT_NAME))) {
+ nonEditables.add(name);
+ sb.append(values.getOrDefault(name, ""));
+ } else {
+ if (defaultValue != null) {
+ sb.append("${").append(placeholders.computeIfAbsent(name, n -> last.incrementAndGet())).append(':').append(defaultValue).append('}');
+ } else {
+ sb.append('$').append(placeholders.computeIfAbsent(name, n -> last.incrementAndGet()));
+ }
+ }
+ }
+ }
+ idx = matcher.end();
+ }
+ String tail = s.substring(idx);
+ return sb.append(tail.endsWith("\n") ? tail.substring(0, tail.length() - 1) : tail).toString();
+ }
+
+ private static Map<String, String> getHints(String text) {
+ Map<String, String> hint2Values = new HashMap<>();
+ Matcher matcher = SNIPPET_HINT_PATTERN.matcher(text);
+ int idx = 0;
+ while (matcher.find(idx)) {
+ hint2Values.put(matcher.group(1), matcher.groupCount() > 2 ? matcher.group(3) : matcher.groupCount() > 1 ? matcher.group(2) : null);
+ idx = matcher.end();
+ }
+ return hint2Values;
+ }
+
+ private static String html2text(String html) {
+ try {
+ StringBuilder sb = new StringBuilder();
+ new ParserDelegator().parse(new StringReader(html), new HTMLEditorKit.ParserCallback() {
+ @Override
+ public void handleText(char[] text, int pos) {
+ sb.append(text);
+ }
+ }, Boolean.TRUE);
+ return sb.toString();
+ } catch (IOException ex) {
+ return html;
+ }
+ }
+ }
+
private static final class Query extends AsyncCompletionQuery
implements ChangeListener {
@@ -138,6 +260,19 @@ public final class CodeTemplateCompletionProvider implements CompletionProvider
}
protected void query(CompletionResultSet resultSet, Document doc, int caretOffset) {
+ query(doc, caretOffset, (ct, abbrevBased) -> {
+ if (queryResult != null) {
+ queryResult.add(new CodeTemplateCompletionItem(ct, abbrevBased));
+ }
+ });
+ if (queryResult != null) {
+ resultSet.addAllItems(queryResult);
+ }
+ resultSet.setAnchorOffset(queryAnchorOffset);
+ resultSet.finish();
+ }
+
+ private void query(Document doc, int caretOffset, BiConsumer<CodeTemplate, Boolean> consumer) {
String langPath = null;
String identifierBeforeCursor = null;
if (doc instanceof AbstractDocument) {
@@ -171,7 +306,7 @@ public final class CodeTemplateCompletionProvider implements CompletionProvider
queryCaretOffset = caretOffset;
queryAnchorOffset = caretOffset - identifierBeforeCursor.length();
if (langPath != null) {
- String mimeType = DocumentUtilities.getMimeType(component);
+ String mimeType = DocumentUtilities.getMimeType(doc);
MimePath mimePath = mimeType == null ? MimePath.EMPTY : MimePath.get(mimeType);
Preferences prefs = MimeLookup.getLookup(mimePath).lookup(Preferences.class);
boolean ignoreCase = prefs.getBoolean(SimpleValueNames.COMPLETION_CASE_SENSITIVE, false);
@@ -181,25 +316,21 @@ public final class CodeTemplateCompletionProvider implements CompletionProvider
Collection<? extends CodeTemplate> ctsPT = op.findByParametrizedText(identifierBeforeCursor, ignoreCase);
Collection<? extends CodeTemplate> ctsAb = op.findByAbbreviationPrefix(identifierBeforeCursor, ignoreCase);
- Collection<? extends CodeTemplateFilter> filters = CodeTemplateManagerOperation.getTemplateFilters(component, queryAnchorOffset);
+ Collection<? extends CodeTemplateFilter> filters = CodeTemplateManagerOperation.getTemplateFilters(doc, queryAnchorOffset, queryAnchorOffset);
queryResult = new ArrayList<CodeTemplateCompletionItem>(ctsPT.size() + ctsAb.size());
Set<String> abbrevs = new HashSet<String>(ctsPT.size() + ctsAb.size());
for (CodeTemplate ct : ctsPT) {
if (ct.getContexts() != null && ct.getContexts().size() > 0 && accept(ct, filters) && abbrevs.add(ct.getAbbreviation())) {
- queryResult.add(new CodeTemplateCompletionItem(ct, false));
+ consumer.accept(ct, false);
}
}
for (CodeTemplate ct : ctsAb) {
if (ct.getContexts() != null && ct.getContexts().size() > 0 && accept(ct, filters) && abbrevs.add(ct.getAbbreviation())) {
- queryResult.add(new CodeTemplateCompletionItem(ct, true));
+ consumer.accept(ct, true);
}
}
- resultSet.addAllItems(queryResult);
}
-
- resultSet.setAnchorOffset(queryAnchorOffset);
- resultSet.finish();
}
public void stateChanged(ChangeEvent evt) {
diff --git a/ide/editor.codetemplates/src/org/netbeans/lib/editor/codetemplates/CodeTemplateManagerOperation.java b/ide/editor.codetemplates/src/org/netbeans/lib/editor/codetemplates/CodeTemplateManagerOperation.java
index 3233b97..edc4edb 100644
--- a/ide/editor.codetemplates/src/org/netbeans/lib/editor/codetemplates/CodeTemplateManagerOperation.java
+++ b/ide/editor.codetemplates/src/org/netbeans/lib/editor/codetemplates/CodeTemplateManagerOperation.java
@@ -251,14 +251,14 @@ public final class CodeTemplateManagerOperation
return result;
}
- public static Collection<? extends CodeTemplateFilter> getTemplateFilters(JTextComponent component, int offset) {
- MimePath mimeType = getFullMimePath(component.getDocument(), offset);
+ public static Collection<? extends CodeTemplateFilter> getTemplateFilters(Document doc, int startOffset, int endOffset) {
+ MimePath mimeType = getFullMimePath(doc, startOffset);
Collection<? extends CodeTemplateFilter.Factory> filterFactories =
MimeLookup.getLookup(mimeType).lookupAll(CodeTemplateFilter.Factory.class);
List<CodeTemplateFilter> result = new ArrayList<CodeTemplateFilter>(filterFactories.size());
for (CodeTemplateFilter.Factory factory : filterFactories) {
- result.add(factory.createFilter(component, offset));
+ result.add(factory.createFilter(doc, startOffset, endOffset));
}
return result;
}
diff --git a/ide/editor.codetemplates/src/org/netbeans/lib/editor/codetemplates/SurroundWithFix.java b/ide/editor.codetemplates/src/org/netbeans/lib/editor/codetemplates/SurroundWithFix.java
index 7372be8..5cdb310 100644
--- a/ide/editor.codetemplates/src/org/netbeans/lib/editor/codetemplates/SurroundWithFix.java
+++ b/ide/editor.codetemplates/src/org/netbeans/lib/editor/codetemplates/SurroundWithFix.java
@@ -46,7 +46,7 @@ public class SurroundWithFix implements Fix {
CodeTemplateManagerOperation op = CodeTemplateManagerOperation.get(component.getDocument(), component.getSelectionStart());
if (op != null) {
op.waitLoaded();
- Collection<? extends CodeTemplateFilter> filters = CodeTemplateManagerOperation.getTemplateFilters(component, component.getSelectionStart());
+ Collection<? extends CodeTemplateFilter> filters = CodeTemplateManagerOperation.getTemplateFilters(component.getDocument(), component.getSelectionStart(), component.getSelectionEnd());
for (CodeTemplate template : op.findSelectionTemplates()) {
// for surround-with use also templates that have no contexts.
// They are usually user-defined, see #118996.
diff --git a/ide/editor.codetemplates/src/org/netbeans/lib/editor/codetemplates/spi/CodeTemplateFilter.java b/ide/editor.codetemplates/src/org/netbeans/lib/editor/codetemplates/spi/CodeTemplateFilter.java
index 7e04f16..9ec76ad 100644
--- a/ide/editor.codetemplates/src/org/netbeans/lib/editor/codetemplates/spi/CodeTemplateFilter.java
+++ b/ide/editor.codetemplates/src/org/netbeans/lib/editor/codetemplates/spi/CodeTemplateFilter.java
@@ -20,6 +20,8 @@
package org.netbeans.lib.editor.codetemplates.spi;
import java.util.List;
+import javax.swing.JTextArea;
+import javax.swing.text.Document;
import javax.swing.text.JTextComponent;
import org.netbeans.lib.editor.codetemplates.api.CodeTemplate;
import org.netbeans.spi.editor.mimelookup.MimeLocation;
@@ -56,6 +58,22 @@ public interface CodeTemplateFilter {
* @return non-null code template filter instance.
*/
CodeTemplateFilter createFilter(JTextComponent component, int offset);
+
+ /**
+ * Create code template filter for the given context.
+ *
+ * @param doc non-null document for which the filter is being created.
+ * @param startOffset >=0 start offset for which the filter is being created.
+ * @param endOffset >=startOffset end offset for which the filter is being created.
+ * @return non-null code template filter instance.
+ * @since 1.57
+ */
+ default CodeTemplateFilter createFilter(Document doc, int startOffset, int endOffset) {
+ JTextArea component = new JTextArea(doc);
+ component.setSelectionStart(startOffset);
+ component.setSelectionEnd(endOffset);
+ return createFilter(component, startOffset);
+ }
}
/**
diff --git a/java/java.editor/src/org/netbeans/modules/editor/java/JavaCodeTemplateFilter.java b/java/java.editor/src/org/netbeans/modules/editor/java/JavaCodeTemplateFilter.java
index bbfbe4f..c93e2b3 100644
--- a/java/java.editor/src/org/netbeans/modules/editor/java/JavaCodeTemplateFilter.java
+++ b/java/java.editor/src/org/netbeans/modules/editor/java/JavaCodeTemplateFilter.java
@@ -26,6 +26,7 @@ import java.util.EnumSet;
import java.util.HashSet;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
+import javax.swing.text.Document;
import javax.swing.text.JTextComponent;
import com.sun.source.tree.CaseTree;
import com.sun.source.tree.ClassTree;
@@ -65,11 +66,9 @@ public class JavaCodeTemplateFilter implements CodeTemplateFilter {
private Tree.Kind treeKindCtx = null;
private String stringCtx = null;
- private JavaCodeTemplateFilter(JTextComponent component, int offset) {
- if (Utilities.isJavaContext(component, offset, true)) {
- final int startOffset = offset;
- final int endOffset = component.getSelectionStart() == offset ? component.getSelectionEnd() : -1;
- final Source source = Source.create(component.getDocument());
+ private JavaCodeTemplateFilter(Document doc, int startOffset, int endOffset) {
+ if (Utilities.isJavaContext(doc, startOffset, true)) {
+ final Source source = Source.create(doc);
if (source != null) {
final AtomicBoolean cancel = new AtomicBoolean();
BaseProgressUtils.runOffEventDispatchThread(() -> {
@@ -216,7 +215,12 @@ public class JavaCodeTemplateFilter implements CodeTemplateFilter {
@Override
public CodeTemplateFilter createFilter(JTextComponent component, int offset) {
- return new JavaCodeTemplateFilter(component, offset);
+ return createFilter(component.getDocument(), offset, component.getSelectionStart() == offset ? component.getSelectionEnd() : -1);
+ }
+
+ @Override
+ public CodeTemplateFilter createFilter(Document doc, int startOffset, int endOffset) {
+ return new JavaCodeTemplateFilter(doc, startOffset, endOffset);
}
@Override
diff --git a/java/java.editor/src/org/netbeans/modules/editor/java/Utilities.java b/java/java.editor/src/org/netbeans/modules/editor/java/Utilities.java
index 4aaa7cf..ef66a8d 100644
--- a/java/java.editor/src/org/netbeans/modules/editor/java/Utilities.java
+++ b/java/java.editor/src/org/netbeans/modules/editor/java/Utilities.java
@@ -235,7 +235,10 @@ public final class Utilities {
}
public static boolean isJavaContext(final JTextComponent component, final int offset, final boolean allowInStrings) {
- Document doc = component.getDocument();
+ return isJavaContext(component.getDocument(), offset, allowInStrings);
+ }
+
+ public static boolean isJavaContext(final Document doc, final int offset, final boolean allowInStrings) {
if (doc instanceof AbstractDocument) {
((AbstractDocument)doc).readLock();
}
diff --git a/java/java.editor/src/org/netbeans/modules/java/editor/resources/DefaultAbbrevs.xml b/java/java.editor/src/org/netbeans/modules/java/editor/resources/DefaultAbbrevs.xml
index 0ed5b15..45d9e3c 100644
--- a/java/java.editor/src/org/netbeans/modules/java/editor/resources/DefaultAbbrevs.xml
+++ b/java/java.editor/src/org/netbeans/modules/java/editor/resources/DefaultAbbrevs.xml
@@ -58,18 +58,26 @@
<codetemplate abbreviation="serr" contexts="BLOCK,CASE,LABELED_STATEMENT,DO_WHILE_LOOP,ENHANCED_FOR_LOOP,FOR_LOOP,IF,WHILE_LOOP,LAMBDA_EXPRESSION"><code><![CDATA[System.err.println("${cursor}");]]></code></codetemplate>
<codetemplate abbreviation="sout" contexts="BLOCK,CASE,LABELED_STATEMENT,DO_WHILE_LOOP,ENHANCED_FOR_LOOP,FOR_LOOP,IF,WHILE_LOOP,LAMBDA_EXPRESSION"><code><![CDATA[System.out.println("${cursor}");]]></code></codetemplate>
<codetemplate abbreviation="st"><code><![CDATA[${no-indent}static ]]></code></codetemplate>
- <codetemplate abbreviation="sw"><code><![CDATA[switch (${var instanceof="java.lang.Enum"}) {
- case ${val completionInvoke}:
- ${cursor}
- break;
- default:
- throw new AssertionError();
- }
-]]></code></codetemplate>
- <codetemplate abbreviation="cs"><code><![CDATA[case ${what completionInvoke}:
- ${cursor}
- break;
-]]></code></codetemplate>
+ <codetemplate abbreviation="sw" contexts="BLOCK,CASE,LABELED_STATEMENT,DO_WHILE_LOOP,ENHANCED_FOR_LOOP,FOR_LOOP,IF,WHILE_LOOP,LAMBDA_EXPRESSION">
+ <code>
+<![CDATA[switch (${var instanceof="java.lang.Enum"}) {
+ case ${val completionInvoke}:
+ ${selection}${cursor}
+ break;
+ default:
+ throw new AssertionError();
+}
+]]>
+ </code>
+ </codetemplate>
+ <codetemplate abbreviation="cs" contexts="CASE,SWITCH">
+ <code>
+<![CDATA[case ${what completionInvoke}:
+ ${selection}${cursor}
+ break;
+]]>
+ </code>
+ </codetemplate>
<codetemplate abbreviation="tw"><code><![CDATA[throw ${exc instanceof="java.lang.Throwable" default="new IllegalStateException()"};]]></code></codetemplate>
<codetemplate abbreviation="ise"><code><![CDATA[throw new IllegalStateException("${arg}");]]></code></codetemplate>
<codetemplate abbreviation="iae"><code><![CDATA[throw new IllegalArgumentException("${arg}");]]></code></codetemplate>
@@ -79,9 +87,9 @@
}
]]></code></codetemplate>
<codetemplate abbreviation="do"><code><![CDATA[do {
- ${cursor}
- } while (${expr instanceof="boolean" default="true"});
- ]]></code></codetemplate>
+ ${cursor}
+ } while (${expr instanceof="boolean" default="true"});
+]]></code></codetemplate>
<codetemplate abbreviation="sy"><code><![CDATA[${no-indent}synchronized ]]></code></codetemplate>
<codetemplate abbreviation="tds"><code><![CDATA[Thread.dumpStack();]]></code></codetemplate>
@@ -108,7 +116,7 @@
<codetemplate abbreviation="for" contexts="BLOCK,CASE,LABELED_STATEMENT,DO_WHILE_LOOP,ENHANCED_FOR_LOOP,FOR_LOOP,IF,WHILE_LOOP,LAMBDA_EXPRESSION">
<code>
<![CDATA[for (int ${IDX newVarName default="i"} = 0; ${IDX} < ${MAX default="10"}; ${IDX}++) {
-${selection}${cursor}
+ ${selection}${cursor}
}
]]>
</code>
@@ -117,8 +125,8 @@ ${selection}${cursor}
<codetemplate abbreviation="fori" contexts="BLOCK,CASE,LABELED_STATEMENT,DO_WHILE_LOOP,ENHANCED_FOR_LOOP,FOR_LOOP,IF,WHILE_LOOP,LAMBDA_EXPRESSION">
<code>
<![CDATA[for (int ${IDX newVarName default="idx"} = 0; ${IDX} < ${ARR array default="arr"}.length; ${IDX}++) {
- ${TYPE rightSideType default="Object"} ${ELEM newVarName default="elem"} = ${TYPE_CAST cast default="" editable=false}${ARR}[${IDX}];
-${selection}${cursor}
+ ${TYPE rightSideType default="Object"} ${ELEM newVarName default="elem"} = ${TYPE_CAST cast default="" editable=false}${ARR}[${IDX}];
+ ${selection}${cursor}
}
]]>
</code>
@@ -126,8 +134,8 @@ ${selection}${cursor}
<codetemplate abbreviation="forc" contexts="BLOCK,CASE,LABELED_STATEMENT,DO_WHILE_LOOP,ENHANCED_FOR_LOOP,FOR_LOOP,IF,WHILE_LOOP,LAMBDA_EXPRESSION">
<code>
<![CDATA[for (${IT_TYPE rightSideType type="java.util.Iterator" default="Iterator" editable=false} ${IT newVarName default="it"} = ${COL instanceof="java.util.Collection" default="col"}.iterator(); ${IT}.hasNext();) {
- ${TYPE rightSideType default="Object"} ${ELEM newVarName default="elem"} = ${TYPE_CAST cast default="" editable=false}${IT}.next();
-${selection}${cursor}
+ ${TYPE rightSideType default="Object"} ${ELEM newVarName default="elem"} = ${TYPE_CAST cast default="" editable=false}${IT}.next();
+ ${selection}${cursor}
}
]]>
</code>
@@ -135,8 +143,8 @@ ${selection}${cursor}
<codetemplate abbreviation="forl" contexts="BLOCK,CASE,LABELED_STATEMENT,DO_WHILE_LOOP,ENHANCED_FOR_LOOP,FOR_LOOP,IF,WHILE_LOOP,LAMBDA_EXPRESSION">
<code>
<![CDATA[for (int ${IDX newVarName default="idx"} = 0; ${IDX} < ${LIST instanceof="java.util.List" default="lst"}.size(); ${IDX}++) {
- ${TYPE rightSideType default="Object"} ${ELEM newVarName default="elem"} = ${TYPE_CAST cast default="" editable=false}${LIST}.get(${IDX});
-${selection}${cursor}
+ ${TYPE rightSideType default="Object"} ${ELEM newVarName default="elem"} = ${TYPE_CAST cast default="" editable=false}${LIST}.get(${IDX});
+ ${selection}${cursor}
}
]]>
</code>
@@ -144,8 +152,8 @@ ${selection}${cursor}
<codetemplate abbreviation="forv" contexts="BLOCK,CASE,LABELED_STATEMENT,DO_WHILE_LOOP,ENHANCED_FOR_LOOP,FOR_LOOP,IF,WHILE_LOOP,LAMBDA_EXPRESSION">
<code>
<![CDATA[for (int ${IDX newVarName default="idx"} = 0; ${IDX} < ${VECTOR instanceof="java.util.Vector" default="vct"}.size(); ${IDX}++) {
- ${TYPE rightSideType default="Object"} ${ELEM newVarName default="elem"} = ${TYPE_CAST cast default="" editable=false}${VECTOR}.elementAt(${IDX});
-${selection}${cursor}
+ ${TYPE rightSideType default="Object"} ${ELEM newVarName default="elem"} = ${TYPE_CAST cast default="" editable=false}${VECTOR}.elementAt(${IDX});
+ ${selection}${cursor}
}
]]>
</code>
@@ -153,7 +161,7 @@ ${selection}${cursor}
<codetemplate abbreviation="fore" contexts="BLOCK,CASE,LABELED_STATEMENT,DO_WHILE_LOOP,ENHANCED_FOR_LOOP,FOR_LOOP,IF,WHILE_LOOP,LAMBDA_EXPRESSION">
<code>
<![CDATA[for (${TYPE iterableElementType default="Object" editable=false} ${ELEM newVarName default="elem" ordering=2} : ${ITER iterable default="col" ordering=1}) {
-${selection}${cursor}
+ ${selection}${cursor}
}
]]>
</code>
@@ -162,7 +170,7 @@ ${selection}${cursor}
<code>
<![CDATA[for (${STR_TOK type="java.util.StringTokenizer" default="StringTokenizer" editable=false} ${TOKENIZER newVarName} = new ${STR_TOK}(${STRING instanceof="java.lang.String"}); ${TOKENIZER}.hasMoreTokens();) {
String ${TOKEN default="token"} = ${TOKENIZER}.nextToken();
-${selection}${cursor}
+ ${selection}${cursor}
}
]]>
</code>
@@ -170,8 +178,8 @@ ${selection}${cursor}
<codetemplate abbreviation="inst" contexts="BLOCK,CASE,LABELED_STATEMENT,DO_WHILE_LOOP,ENHANCED_FOR_LOOP,FOR_LOOP,IF,WHILE_LOOP,LAMBDA_EXPRESSION">
<code>
<![CDATA[if (${EXP instanceof="java.lang.Object" default="exp"} instanceof ${TYPE default="Object"}) {
- ${TYPE} ${VAR newVarName default="obj"} = (${TYPE})${EXP};
- ${cursor}
+ ${TYPE} ${VAR newVarName default="obj"} = (${TYPE})${EXP};
+ ${cursor}
}
]]>
</code>
@@ -184,8 +192,8 @@ ${selection}${cursor}
<codetemplate abbreviation="whilen" contexts="BLOCK,CASE,LABELED_STATEMENT,DO_WHILE_LOOP,ENHANCED_FOR_LOOP,FOR_LOOP,IF,WHILE_LOOP,LAMBDA_EXPRESSION">
<code>
<![CDATA[while (${ENUM instanceof="java.util.Enumeration" default="en"}.hasMoreElements()) {
- ${TYPE rightSideType default="Object"} ${ELEM newVarName default="elem"} = ${TYPE_CAST cast default="" editable=false} ${ENUM}.nextElement();
-${selection}${cursor}
+ ${TYPE rightSideType default="Object"} ${ELEM newVarName default="elem"} = ${TYPE_CAST cast default="" editable=false} ${ENUM}.nextElement();
+ ${selection}${cursor}
}
]]>
</code>
@@ -193,8 +201,8 @@ ${selection}${cursor}
<codetemplate abbreviation="whileit" contexts="BLOCK,CASE,LABELED_STATEMENT,DO_WHILE_LOOP,ENHANCED_FOR_LOOP,FOR_LOOP,IF,WHILE_LOOP,LAMBDA_EXPRESSION">
<code>
<![CDATA[while (${IT instanceof="java.util.Iterator" default="it"}.hasNext()) {
- ${TYPE rightSideType default="Object"} ${ELEM newVarName default="elem"} = ${TYPE_CAST cast default="" editable=false} ${IT}.next();
-${selection}${cursor}
+ ${TYPE rightSideType default="Object"} ${ELEM newVarName default="elem"} = ${TYPE_CAST cast default="" editable=false} ${IT}.next();
+ ${selection}${cursor}
}
]]>
</code>
@@ -202,7 +210,7 @@ ${selection}${cursor}
<codetemplate abbreviation="iff" contexts="BLOCK,CASE,LABELED_STATEMENT,DO_WHILE_LOOP,ENHANCED_FOR_LOOP,FOR_LOOP,IF,WHILE_LOOP,LAMBDA_EXPRESSION" descriptionId="CT_iff">
<code>
<![CDATA[if (${EXP instanceof="boolean" default="true"}) {
-${selection}${cursor}
+ ${selection}${cursor}
}
]]>
</code>
@@ -210,7 +218,7 @@ ${selection}${cursor}
<codetemplate abbreviation="ifelse" contexts="BLOCK,CASE,LABELED_STATEMENT,DO_WHILE_LOOP,ENHANCED_FOR_LOOP,FOR_LOOP,IF,WHILE_LOOP,LAMBDA_EXPRESSION" descriptionId="CT_ifelse">
<code>
<![CDATA[if (${EXP instanceof="boolean" default="true"}) {
-${selection}${cursor}
+ ${selection}${cursor}
} else {
}
]]>
@@ -219,7 +227,7 @@ ${selection}${cursor}
<codetemplate abbreviation="whilexp" contexts="BLOCK,CASE,LABELED_STATEMENT,DO_WHILE_LOOP,ENHANCED_FOR_LOOP,FOR_LOOP,IF,WHILE_LOOP,LAMBDA_EXPRESSION">
<code>
<![CDATA[while (${EXP instanceof="boolean" default="true"}) {
-${selection}${cursor}
+ ${selection}${cursor}
}
]]>
</code>
@@ -227,7 +235,7 @@ ${selection}${cursor}
<codetemplate abbreviation="dowhile" contexts="BLOCK,CASE,LABELED_STATEMENT,DO_WHILE_LOOP,ENHANCED_FOR_LOOP,FOR_LOOP,IF,WHILE_LOOP,LAMBDA_EXPRESSION">
<code>
<![CDATA[do {
-${selection}${cursor}
+ ${selection}${cursor}
} while (${EXP instanceof="boolean" default="true"});
]]>
</code>
@@ -235,9 +243,9 @@ ${selection}${cursor}
<codetemplate abbreviation="runn" contexts="BLOCK,CASE,LABELED_STATEMENT">
<code>
<![CDATA[${RUNN_TYPE type="java.lang.Runnable" default="Runnable" editable=false} ${RUNN newVarName default="r"} = new ${RUNN_TYPE}() {
- public void run() {
-${selection}${cursor}
- }
+ public void run() {
+ ${selection}${cursor}
+ }
};
]]>
</code>
@@ -245,7 +253,7 @@ ${selection}${cursor}
<codetemplate abbreviation="trycatch" contexts="BLOCK,CASE,LABELED_STATEMENT,DO_WHILE_LOOP,ENHANCED_FOR_LOOP,FOR_LOOP,IF,WHILE_LOOP,LAMBDA_EXPRESSION">
<code>
<![CDATA[try {
-${selection}${cursor}
+ ${selection}${cursor}
} ${CATCH_STMTS uncaughtExceptionCatchStatements default="catch (Exception e) {}" editable=false}
]]>
</code>
@@ -259,7 +267,7 @@ ${selection}${cursor}
<codetemplate abbreviation="psvm" contexts="CLASS">
<code>
<![CDATA[public static void main(String[] args) {
- ${cursor}
+ ${cursor}
}
]]>
</code>
@@ -268,7 +276,7 @@ ${selection}${cursor}
<codetemplate abbreviation="main" contexts="CLASS">
<code>
<![CDATA[public static void main(String[] args) {
- ${cursor}
+ ${cursor}
}
]]>
</code>
@@ -322,7 +330,7 @@ ${cursor}]]></code></codetemplate>
<![CDATA[for (${entryType iterableElementType default="Map.Entry<Object, Object>" editable=false} ${entry newVarName default="en"} : ${map instanceof="java.util.Map" default="m"}.entrySet()) {
${KeyType rightSideType default="Object"} ${key newVarName default="key"} = ${entry}.getKey();
${ValType rightSideType default="Object"} ${val newVarName default="val"} = ${entry}.getValue();
-${selection}${cursor}
+ ${selection}${cursor}
}]]>
</code>
</codetemplate>
diff --git a/java/java.lsp.server/nbproject/project.xml b/java/java.lsp.server/nbproject/project.xml
index 9e4f7a3..ce9ea03 100644
--- a/java/java.lsp.server/nbproject/project.xml
+++ b/java/java.lsp.server/nbproject/project.xml
@@ -139,6 +139,15 @@
</run-dependency>
</dependency>
<dependency>
+ <code-name-base>org.netbeans.modules.editor.codetemplates</code-name-base>
+ <build-prerequisite/>
+ <compile-dependency/>
+ <run-dependency>
+ <release-version>1</release-version>
+ <specification-version>1.56</specification-version>
+ </run-dependency>
+ </dependency>
+ <dependency>
<code-name-base>org.netbeans.modules.editor.document</code-name-base>
<build-prerequisite/>
<compile-dependency/>
diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/ExtractSuperclassOrInterfaceRefactoring.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/ExtractSuperclassOrInterfaceRefactoring.java
index 970f76a..89700a9 100644
--- a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/ExtractSuperclassOrInterfaceRefactoring.java
+++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/ExtractSuperclassOrInterfaceRefactoring.java
@@ -70,7 +70,7 @@ import org.openide.util.lookup.ServiceProvider;
* @author Dusan Balek
*/
@ServiceProvider(service = CodeActionsProvider.class, position = 170)
-public class ExtractSuperclassOrInterfaceRefactoring extends CodeRefactoring {
+public final class ExtractSuperclassOrInterfaceRefactoring extends CodeRefactoring {
private static final String EXTRACT_SUPERCLASS_REFACTORING_COMMAND = "java.refactor.extract.superclass";
private static final String EXTRACT_INTERFACE_REFACTORING_COMMAND = "java.refactor.extract.interface";
@@ -81,8 +81,8 @@ public class ExtractSuperclassOrInterfaceRefactoring extends CodeRefactoring {
@Override
@NbBundle.Messages({
- "DN_ExtractSuperclass= Extract Superclass...",
- "DN_ExtractInterface= Extract Interface...",
+ "DN_ExtractSuperclass=Extract Superclass...",
+ "DN_ExtractInterface=Extract Interface...",
})
public List<CodeAction> getCodeActions(ResultIterator resultIterator, CodeActionParams params) throws Exception {
List<String> only = params.getContext().getOnly();
@@ -181,7 +181,7 @@ public class ExtractSuperclassOrInterfaceRefactoring extends CodeRefactoring {
}
});
} else {
- client.logMessage(new MessageParams(MessageType.Error, String.format("Illegal number of arguments received for command: %s", command)));
+ client.showMessage(new MessageParams(MessageType.Error, String.format("Illegal number of arguments received for command: %s", command)));
}
return CompletableFuture.completedFuture(true);
}
@@ -232,7 +232,7 @@ public class ExtractSuperclassOrInterfaceRefactoring extends CodeRefactoring {
refactoring.getContext().add(JavaRefactoringUtils.getClasspathInfoFor(file));
client.applyEdit(new ApplyWorkspaceEditParams(perform(refactoring, EXTRACT_SUPERCLASS_REFACTORING_COMMAND.equals(command) ? "Extract Superclass" : "Extract Interface")));
} catch (Exception ex) {
- client.logMessage(new MessageParams(MessageType.Error, ex.getLocalizedMessage()));
+ client.showMessage(new MessageParams(MessageType.Error, ex.getLocalizedMessage()));
}
}
}
diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/MoveRefactoring.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/MoveRefactoring.java
index 72160e9..9aa18d7 100644
--- a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/MoveRefactoring.java
+++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/MoveRefactoring.java
@@ -69,7 +69,7 @@ import org.openide.util.lookup.ServiceProvider;
* @author Dusan Balek
*/
@ServiceProvider(service = CodeActionsProvider.class, position = 160)
-public class MoveRefactoring extends CodeRefactoring {
+public final class MoveRefactoring extends CodeRefactoring {
private static final String MOVE_REFACTORING_KIND = "refactor.move";
private static final String MOVE_REFACTORING_COMMAND = "java.refactor.move";
@@ -80,7 +80,7 @@ public class MoveRefactoring extends CodeRefactoring {
@Override
@NbBundle.Messages({
- "DN_Move= Move...",
+ "DN_Move=Move...",
})
public List<CodeAction> getCodeActions(ResultIterator resultIterator, CodeActionParams params) throws Exception {
List<String> only = params.getContext().getOnly();
@@ -181,7 +181,7 @@ public class MoveRefactoring extends CodeRefactoring {
throw new IllegalArgumentException(String.format("Illegal number of arguments received for command: %s", command));
}
} catch (Exception ex) {
- client.logMessage(new MessageParams(MessageType.Error, ex.getLocalizedMessage()));
+ client.showMessage(new MessageParams(MessageType.Error, ex.getLocalizedMessage()));
}
return CompletableFuture.completedFuture(true);
}
@@ -206,7 +206,7 @@ public class MoveRefactoring extends CodeRefactoring {
}
client.applyEdit(new ApplyWorkspaceEditParams(perform(refactoring, "Move")));
} catch (Exception ex) {
- client.logMessage(new MessageParams(MessageType.Error, ex.getLocalizedMessage()));
+ client.showMessage(new MessageParams(MessageType.Error, ex.getLocalizedMessage()));
}
}
diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/PullUpRefactoring.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/PullUpRefactoring.java
index 2beb73e..b146688 100644
--- a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/PullUpRefactoring.java
+++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/PullUpRefactoring.java
@@ -67,7 +67,7 @@ import org.openide.util.lookup.ServiceProvider;
* @author Dusan Balek
*/
@ServiceProvider(service = CodeActionsProvider.class, position = 180)
-public class PullUpRefactoring extends CodeRefactoring {
+public final class PullUpRefactoring extends CodeRefactoring {
private static final String PULL_UP_REFACTORING_KIND = "refactor.pull.up";
private static final String PULL_UP_REFACTORING_COMMAND = "java.refactor.pull.up";
@@ -77,7 +77,7 @@ public class PullUpRefactoring extends CodeRefactoring {
@Override
@NbBundle.Messages({
- "DN_PullUp= Pull Up...",
+ "DN_PullUp=Pull Up...",
})
public List<CodeAction> getCodeActions(ResultIterator resultIterator, CodeActionParams params) throws Exception {
List<String> only = params.getContext().getOnly();
@@ -164,7 +164,7 @@ public class PullUpRefactoring extends CodeRefactoring {
throw new IllegalArgumentException(String.format("Illegal number of arguments received for command: %s", command));
}
} catch (Exception ex) {
- client.logMessage(new MessageParams(MessageType.Error, ex.getLocalizedMessage()));
+ client.showMessage(new MessageParams(MessageType.Error, ex.getLocalizedMessage()));
}
return CompletableFuture.completedFuture(true);
}
@@ -206,7 +206,7 @@ public class PullUpRefactoring extends CodeRefactoring {
}
}, true);
} catch (Exception ex) {
- client.logMessage(new MessageParams(MessageType.Error, ex.getLocalizedMessage()));
+ client.showMessage(new MessageParams(MessageType.Error, ex.getLocalizedMessage()));
}
return members;
}
@@ -238,7 +238,7 @@ public class PullUpRefactoring extends CodeRefactoring {
refactoring.getContext().add(JavaRefactoringUtils.getClasspathInfoFor(file));
client.applyEdit(new ApplyWorkspaceEditParams(perform(refactoring, "PullUp")));
} catch (Exception ex) {
- client.logMessage(new MessageParams(MessageType.Error, ex.getLocalizedMessage()));
+ client.showMessage(new MessageParams(MessageType.Error, ex.getLocalizedMessage()));
}
}
diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/PushDownRefactoring.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/PushDownRefactoring.java
index edb4500..591f071 100644
--- a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/PushDownRefactoring.java
+++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/PushDownRefactoring.java
@@ -28,6 +28,7 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
+import java.util.EnumSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
@@ -41,6 +42,7 @@ import org.eclipse.lsp4j.CodeActionKind;
import org.eclipse.lsp4j.CodeActionParams;
import org.eclipse.lsp4j.MessageParams;
import org.eclipse.lsp4j.MessageType;
+import org.netbeans.api.java.source.ClassIndex;
import org.netbeans.api.java.source.ClasspathInfo;
import org.netbeans.api.java.source.CompilationController;
import org.netbeans.api.java.source.CompilationInfo;
@@ -61,7 +63,7 @@ import org.openide.util.lookup.ServiceProvider;
* @author Dusan Balek
*/
@ServiceProvider(service = CodeActionsProvider.class, position = 190)
-public class PushDownRefactoring extends CodeRefactoring {
+public final class PushDownRefactoring extends CodeRefactoring {
private static final String PUSH_DOWN_REFACTORING_KIND = "refactor.push.down";
private static final String PUSH_DOWN_REFACTORING_COMMAND = "java.refactor.push.down";
@@ -71,7 +73,7 @@ public class PushDownRefactoring extends CodeRefactoring {
@Override
@NbBundle.Messages({
- "DN_PushDown= Push Down...",
+ "DN_PushDown=Push Down...",
})
public List<CodeAction> getCodeActions(ResultIterator resultIterator, CodeActionParams params) throws Exception {
List<String> only = params.getContext().getOnly();
@@ -100,6 +102,11 @@ public class PushDownRefactoring extends CodeRefactoring {
if (!(element instanceof TypeElement)) {
return Collections.emptyList();
}
+ ElementHandle<TypeElement> eh = ElementHandle.create((TypeElement) element);
+ Set<FileObject> resources = info.getClasspathInfo().getClassIndex().getResources(eh, EnumSet.of(ClassIndex.SearchKind.IMPLEMENTORS), EnumSet.of(ClassIndex.SearchScope.SOURCE));
+ if (resources.isEmpty()) {
+ return Collections.emptyList();
+ }
List<QuickPickItem> members = new ArrayList<>();
for (Element m: element.getEnclosedElements()) {
if (m.getKind() == ElementKind.CONSTRUCTOR || m.getKind() == ElementKind.STATIC_INIT || m.getKind() == ElementKind.INSTANCE_INIT) {
@@ -146,7 +153,7 @@ public class PushDownRefactoring extends CodeRefactoring {
throw new IllegalArgumentException(String.format("Illegal number of arguments received for command: %s", command));
}
} catch (Exception ex) {
- client.logMessage(new MessageParams(MessageType.Error, ex.getLocalizedMessage()));
+ client.showMessage(new MessageParams(MessageType.Error, ex.getLocalizedMessage()));
}
return CompletableFuture.completedFuture(true);
}
@@ -174,7 +181,7 @@ public class PushDownRefactoring extends CodeRefactoring {
refactoring.getContext().add(JavaRefactoringUtils.getClasspathInfoFor(file));
client.applyEdit(new ApplyWorkspaceEditParams(perform(refactoring, "PushDown")));
} catch (Exception ex) {
- client.logMessage(new MessageParams(MessageType.Error, ex.getLocalizedMessage()));
+ client.showMessage(new MessageParams(MessageType.Error, ex.getLocalizedMessage()));
}
}
diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/SurroundWithHint.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/SurroundWithHint.java
new file mode 100644
index 0000000..904815d
--- /dev/null
+++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/SurroundWithHint.java
@@ -0,0 +1,329 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.java.lsp.server.protocol;
+
+import com.sun.source.tree.BlockTree;
+import com.sun.source.tree.IdentifierTree;
+import com.sun.source.tree.StatementTree;
+import com.sun.source.tree.Tree;
+import com.sun.source.tree.VariableTree;
+import com.sun.source.util.SourcePositions;
+import com.sun.source.util.TreePath;
+import com.sun.source.util.Trees;
+import java.io.IOException;
+import java.io.StringReader;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.TypeMirror;
+import javax.swing.text.Document;
+import javax.swing.text.html.HTMLEditorKit;
+import javax.swing.text.html.parser.ParserDelegator;
+import org.eclipse.lsp4j.CodeAction;
+import org.eclipse.lsp4j.CodeActionKind;
+import org.eclipse.lsp4j.CodeActionParams;
+import org.eclipse.lsp4j.Range;
+import org.eclipse.lsp4j.TextEdit;
+import org.eclipse.lsp4j.WorkspaceEdit;
+import org.netbeans.api.editor.mimelookup.MimeLookup;
+import org.netbeans.api.java.source.CompilationController;
+import org.netbeans.api.java.source.CompilationInfo;
+import org.netbeans.api.java.source.JavaSource;
+import org.netbeans.api.java.source.TreeUtilities;
+import org.netbeans.api.java.source.support.ErrorAwareTreePathScanner;
+import org.netbeans.lib.editor.codetemplates.api.CodeTemplate;
+import org.netbeans.lib.editor.codetemplates.api.CodeTemplateManager;
+import org.netbeans.lib.editor.codetemplates.spi.CodeTemplateFilter;
+import org.netbeans.lib.editor.codetemplates.spi.CodeTemplateParameter;
+import org.netbeans.lib.editor.util.swing.DocumentUtilities;
+import org.netbeans.modules.java.lsp.server.Utils;
+import org.netbeans.modules.parsing.api.ResultIterator;
+import org.openide.util.NbBundle;
+import org.openide.util.lookup.ServiceProvider;
+
+/**
+ *
+ * @author Dusan Balek
+ */
+@ServiceProvider(service = CodeActionsProvider.class, position = 80)
+public final class SurroundWithHint extends CodeActionsProvider {
+
+ private static final String COMMAND_INSERT_SNIPPET = "editor.action.insertSnippet";
+ private static final String DOTS = "...";
+ private static final String SNIPPET = "snippet";
+ private static final String SELECTION_VAR = "${selection}";
+ private static final String SELECTED_TEXT_VAR = "${0:$TM_SELECTED_TEXT}";
+ private static final Pattern SNIPPET_VAR_PATTERN = Pattern.compile("\\$\\{\\s*([-\\w]++)([^}]*)?}");
+ private static final Pattern SNIPPET_HINT_PATTERN = Pattern.compile("([-\\w]++)(?:\\s*=\\s*(\\\"([^\\\"]*)\\\"|\\S*))?");
+ private static final Set<String> TO_FILTER = Collections.singleton("fcom");
+
+ @Override
+ @NbBundle.Messages({
+ "DN_SurroundWith=Surround with \"{0}\"",
+ })
+ public List<CodeAction> getCodeActions(ResultIterator resultIterator, CodeActionParams params) throws Exception {
+ CompilationController info = CompilationController.get(resultIterator.getParserResult());
+ if (info == null) {
+ return Collections.emptyList();
+ }
+ info.toPhase(JavaSource.Phase.ELEMENTS_RESOLVED);
+ int startOffset = getOffset(info, params.getRange().getStart());
+ int endOffset = getOffset(info, params.getRange().getEnd());
+ if (startOffset >= endOffset) {
+ return Collections.emptyList();
+ }
+ Document doc;
+ try {
+ doc = info.getDocument();
+ } catch (IOException ex) {
+ return Collections.emptyList();
+ }
+ Collection<? extends CodeTemplateFilter> filters = getTemplateFilters(doc, startOffset, endOffset);
+ List<CodeAction> codeActions = new ArrayList<>();
+ for (CodeTemplate codeTemplate : CodeTemplateManager.get(doc).getCodeTemplates()) {
+ String parametrizedText = codeTemplate.getParametrizedText();
+ if (parametrizedText.toLowerCase().contains(SELECTION_VAR) && !TO_FILTER.contains(codeTemplate.getAbbreviation())) {
+ if (codeTemplate.getContexts() == null || codeTemplate.getContexts().isEmpty() || accept(codeTemplate, filters)) {
+ String text = convert(codeTemplate.getParametrizedText(), null);
+ String label = html2text(codeTemplate.getDescription());
+ if (label == null) {
+ int idx = text.indexOf('\n');
+ label = idx < 0 ? text : text.substring(0, idx) + DOTS;
+ }
+ StringBuilder sb = new StringBuilder();
+ AtomicInteger last = new AtomicInteger();
+ List<TextEdit> edits = updateSelection(info, startOffset, endOffset, text, sb, last);
+ String snippet = convert(codeTemplate.getParametrizedText(), last);
+ if (sb.length() > 0) {
+ snippet = sb.append(snippet).toString();
+ }
+ CodeAction codeAction = createCodeAction(Bundle.DN_SurroundWith(label), CodeActionKind.RefactorRewrite, COMMAND_INSERT_SNIPPET, Collections.singletonMap(SNIPPET, snippet));
+ if (!edits.isEmpty()) {
+ codeAction.setEdit(new WorkspaceEdit(Collections.singletonMap(params.getTextDocument().getUri(), edits)));
+ }
+ codeActions.add(codeAction);
+ }
+ }
+ }
+ return codeActions;
+ }
+
+ @Override
+ public Set<String> getCommands() {
+ return Collections.emptySet();
+ }
+
+ @Override
+ public CompletableFuture<Object> processCommand(NbCodeLanguageClient client, String command, List<Object> arguments) {
+ return CompletableFuture.completedFuture(false);
+ }
+
+ private static Collection<? extends CodeTemplateFilter> getTemplateFilters(Document doc, int startOffset, int endOffset) {
+ String mimeType = DocumentUtilities.getMimeType(doc);
+ Collection<? extends CodeTemplateFilter.Factory> filterFactories = MimeLookup.getLookup(mimeType).lookupAll(CodeTemplateFilter.Factory.class);
+ List<CodeTemplateFilter> result = new ArrayList<>(filterFactories.size());
+ for (CodeTemplateFilter.Factory factory : filterFactories) {
+ result.add(factory.createFilter(doc, startOffset, endOffset));
+ }
+ return result;
+ }
+
+ private static boolean accept(CodeTemplate template, Collection<? extends CodeTemplateFilter> filters) {
+ for(CodeTemplateFilter filter : filters) {
+ if (!filter.accept(template)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private static String convert(String s, AtomicInteger last) {
+ StringBuilder sb = new StringBuilder();
+ Matcher matcher = SNIPPET_VAR_PATTERN.matcher(s);
+ int idx = 0;
+ Map<String, Integer> placeholders = new HashMap<>();
+ Map<String, String> values = new HashMap<>();
+ Set<String> nonEditables = new HashSet<>();
+ while (matcher.find(idx)) {
+ int start = matcher.start();
+ sb.append(s.substring(idx, start));
+ String name = matcher.group(1);
+ switch (name) {
+ case CodeTemplateParameter.CURSOR_PARAMETER_NAME:
+ case CodeTemplateParameter.NO_FORMAT_PARAMETER_NAME:
+ case CodeTemplateParameter.NO_INDENT_PARAMETER_NAME:
+ break;
+ case CodeTemplateParameter.SELECTION_PARAMETER_NAME:
+ if (last != null) {
+ sb.append(SELECTED_TEXT_VAR);
+ }
+ break;
+ default:
+ String params = matcher.groupCount() > 1 ? matcher.group(2) : null;
+ if (params == null || params.isEmpty()) {
+ if (last == null || nonEditables.contains(name)) {
+ sb.append(values.getOrDefault(name, ""));
+ } else {
+ sb.append('$').append(placeholders.computeIfAbsent(name, n -> last.incrementAndGet()));
+ }
+ } else {
+ Map<String, String> hints = getHints(params);
+ String defaultValue = hints.get(CodeTemplateParameter.DEFAULT_VALUE_HINT_NAME);
+ if (defaultValue != null) {
+ values.put(name, defaultValue);
+ }
+ if ("false".equalsIgnoreCase(hints.get(CodeTemplateParameter.EDITABLE_HINT_NAME))) {
+ nonEditables.add(name);
+ sb.append(values.getOrDefault(name, ""));
+ } else {
+ if (defaultValue != null) {
+ if (last == null) {
+ sb.append(defaultValue);
+ } else {
+ sb.append("${").append(placeholders.computeIfAbsent(name, n -> last.incrementAndGet())).append(':').append(defaultValue).append('}');
+ }
+ } else if (last == null) {
+ sb.append(values.getOrDefault(name, ""));
+ } else {
+ sb.append('$').append(placeholders.computeIfAbsent(name, n -> last.incrementAndGet()));
+ }
+ }
+ }
+ }
+ idx = matcher.end();
+ }
+ String tail = s.substring(idx);
+ return sb.append(tail.endsWith("\n") ? tail.substring(0, tail.length() - 1) : tail).toString();
+ }
+
+ private static Map<String, String> getHints(String text) {
+ Map<String, String> hint2Values = new HashMap<>();
+ Matcher matcher = SNIPPET_HINT_PATTERN.matcher(text);
+ int idx = 0;
+ while (matcher.find(idx)) {
+ hint2Values.put(matcher.group(1), matcher.groupCount() > 2 ? matcher.group(3) : matcher.groupCount() > 1 ? matcher.group(2) : null);
+ idx = matcher.end();
+ }
+ return hint2Values;
+ }
+
+ private static String html2text(String html) throws IOException {
+ if (html == null) {
+ return html;
+ }
+ StringBuilder sb = new StringBuilder();
+ new ParserDelegator().parse(new StringReader(html), new HTMLEditorKit.ParserCallback() {
+ @Override
+ public void handleText(char[] text, int pos) {
+ sb.append(text);
+ }
+ }, Boolean.TRUE);
+ return sb.toString();
+ }
+
+ private static List<TextEdit> updateSelection(CompilationInfo info, int startOffset, int endOffset, String templateText, StringBuilder sb, AtomicInteger last) {
+ List<TextEdit> edits = new ArrayList<>();
+ TreeUtilities tu = info.getTreeUtilities();
+ StatementTree stat = tu.parseStatement(templateText, null);
+ EnumSet<Tree.Kind> kinds = EnumSet.of(Tree.Kind.BLOCK, Tree.Kind.DO_WHILE_LOOP,
+ Tree.Kind.ENHANCED_FOR_LOOP, Tree.Kind.FOR_LOOP, Tree.Kind.IF, Tree.Kind.SYNCHRONIZED,
+ Tree.Kind.TRY, Tree.Kind.WHILE_LOOP);
+ if (stat != null && kinds.contains(stat.getKind())) {
+ TreePath treePath = tu.pathFor(startOffset);
+ Tree tree = treePath.getLeaf();
+ if (tree.getKind() == Tree.Kind.BLOCK && tree == tu.pathFor(endOffset).getLeaf()) {
+ Trees trees = info.getTrees();
+ SourcePositions sp = trees.getSourcePositions();
+ Map<VariableElement, VariableTree> vars = new HashMap<>();
+ LinkedList<VariableTree> varList = new LinkedList<>();
+ ErrorAwareTreePathScanner scanner = new ErrorAwareTreePathScanner() {
+ private int cnt = 0;
+ @Override
+ public Object visitIdentifier(IdentifierTree node, Object p) {
+ Element e = trees.getElement(getCurrentPath());
+ VariableTree var;
+ if (e != null && (var = vars.remove(e)) != null) {
+ sb.append(var.getType()).append(' ').append(var.getName());
+ TypeMirror tm = ((VariableElement)e).asType();
+ switch(tm.getKind()) {
+ case ARRAY:
+ case DECLARED:
+ sb.append(" = ${").append(last.incrementAndGet()).append(':').append("\"null\"}");
+ break;
+ case BOOLEAN:
+ sb.append(" = ${").append(last.incrementAndGet()).append(':').append("\"false\"}");
+ break;
+ case BYTE:
+ case CHAR:
+ case DOUBLE:
+ case FLOAT:
+ case INT:
+ case LONG:
+ case SHORT:
+ sb.append(" = ${").append(last.incrementAndGet()).append(':').append("\"0\"}");
+ break;
+ }
+ sb.append(";\n"); //NOI18N
+ }
+ return null;
+ }
+ };
+ for (StatementTree st : ((BlockTree)tree).getStatements()) {
+ if (sp.getStartPosition(info.getCompilationUnit(), st) >= startOffset) {
+ if (sp.getEndPosition(info.getCompilationUnit(), st) <= endOffset) {
+ if (st.getKind() == Tree.Kind.VARIABLE) {
+ Element e = trees.getElement(new TreePath(treePath, st));
+ if (e != null && e.getKind() == ElementKind.LOCAL_VARIABLE) {
+ vars.put((VariableElement)e, (VariableTree)st);
+ varList.addFirst((VariableTree)st);
+ }
+ }
+ } else {
+ scanner.scan(new TreePath(treePath, st), null);
+ }
+ }
+ }
+ Collection<VariableTree> vals = vars.values();
+ for (VariableTree var : varList) {
+ if (!vals.contains(var)) {
+ int start = (int) sp.getStartPosition(info.getCompilationUnit(), var.getType());
+ int[] span = tu.findNameSpan(var);
+ int end = span != null ? span[0] : (int) sp.getEndPosition(info.getCompilationUnit(), var.getType());
+ edits.add(new TextEdit(new Range(Utils.createPosition(info.getCompilationUnit().getLineMap(), start), Utils.createPosition(info.getCompilationUnit().getLineMap(), end)), ""));
+ }
+ }
+ }
+ }
+ return edits;
+ }
+}
diff --git a/java/java.lsp.server/test/unit/src/org/netbeans/modules/java/lsp/server/protocol/ServerTest.java b/java/java.lsp.server/test/unit/src/org/netbeans/modules/java/lsp/server/protocol/ServerTest.java
index 7cfdb35..5b9e704 100644
--- a/java/java.lsp.server/test/unit/src/org/netbeans/modules/java/lsp/server/protocol/ServerTest.java
+++ b/java/java.lsp.server/test/unit/src/org/netbeans/modules/java/lsp/server/protocol/ServerTest.java
@@ -19,6 +19,7 @@
package org.netbeans.modules.java.lsp.server.protocol;
import com.google.gson.Gson;
+import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import java.beans.PropertyChangeListener;
import java.io.File;
@@ -4945,6 +4946,84 @@ public class ServerTest extends NbTestCase {
}
}
+ public void testSurroundWith() throws Exception {
+ File src = new File(getWorkDir(), "Test.java");
+ src.getParentFile().mkdirs();
+ String code = "public class Test {\n" +
+ " public static void main(String[] args) {\n" +
+ " System.out.println(\"Hello World\");\n" +
+ " }\n" +
+ "}\n";
+ try (Writer w = new FileWriter(src)) {
+ w.write(code);
+ }
+
+ List<Diagnostic>[] diags = new List[1];
+ Launcher<LanguageServer> serverLauncher = LSPLauncher.createClientLauncher(new LspClient() {
+ @Override
+ public void telemetryEvent(Object arg0) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public void publishDiagnostics(PublishDiagnosticsParams params) {
+ synchronized (diags) {
+ diags[0] = params.getDiagnostics();
+ diags.notifyAll();
+ }
+ }
+
+ @Override
+ public void showMessage(MessageParams arg0) {
+ }
+
+ @Override
+ public CompletableFuture<MessageActionItem> showMessageRequest(ShowMessageRequestParams arg0) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public void logMessage(MessageParams arg0) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public CompletableFuture<ApplyWorkspaceEditResponse> applyEdit(ApplyWorkspaceEditParams params) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ }, client.getInputStream(), client.getOutputStream());
+ serverLauncher.startListening();
+ LanguageServer server = serverLauncher.getRemoteProxy();
+ server.initialize(new InitializeParams()).get();
+ String uri = src.toURI().toString();
+ server.getTextDocumentService().didOpen(new DidOpenTextDocumentParams(new TextDocumentItem(uri, "java", 0, code)));
+ synchronized (diags) {
+ while (diags[0] == null) {
+ try {
+ diags.wait();
+ } catch (InterruptedException ex) {
+ }
+ }
+ }
+ VersionedTextDocumentIdentifier id = new VersionedTextDocumentIdentifier(src.toURI().toString(), 1);
+ List<Either<Command, CodeAction>> codeActions = server.getTextDocumentService().codeAction(new CodeActionParams(id, new Range(new Position(2, 8), new Position(2, 42)), new CodeActionContext(diags[0]))).get();
+ Optional<CodeAction> surroundWith =
+ codeActions.stream()
+ .filter(Either::isRight)
+ .map(Either::getRight)
+ .filter(a -> a.getTitle().startsWith(Bundle.DN_SurroundWith("do { ...")))
+ .findAny();
+ assertTrue(surroundWith.isPresent());
+ Command command = surroundWith.get().getCommand();
+ assertEquals("editor.action.insertSnippet", command.getCommand());
+ assertEquals(1, command.getArguments().size());
+ JsonObject obj = (JsonObject) command.getArguments().get(0);
+ assertEquals("do { \n" +
+ " ${0:$TM_SELECTED_TEXT}\n" +
+ "} while (${1:true});", obj.getAsJsonPrimitive("snippet").getAsString());
+ }
+
public void testNoErrorAndHintsFor() throws Exception {
File src = new File(getWorkDir(), "Test.java");
src.getParentFile().mkdirs();
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@netbeans.apache.org
For additional commands, e-mail: commits-help@netbeans.apache.org
For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists