This is an automated email from the ASF dual-hosted git repository.
sdedic 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 96d18cd Completion items imported from Java delegate to
ElementJavadoc to get the content.
new a15d4b6 Merge pull request #2984 from sdedic/groovy/completion-javadoc
96d18cd is described below
commit 96d18cdc3a1e9e9a2edb7db1df931a59de56c226
Author: Svata Dedic <[email protected]>
AuthorDate: Tue Jun 1 16:38:13 2021 +0200
Completion items imported from Java delegate to ElementJavadoc to get the
content.
---
groovy/groovy.editor/nbproject/project.xml | 2 +-
.../editor/api/completion/CompletionHandler.java | 53 +++-
.../editor/api/completion/CompletionItem.java | 113 +++++++-
.../groovy/editor/completion/MethodCompletion.java | 25 +-
.../groovy/editor/completion/TypesCompletion.java | 27 +-
.../completion/provider/CompletionAccessor.java | 64 +++++
.../provider/GroovyElementsProvider.java | 35 ++-
.../completion/provider/JavaElementHandler.java | 59 +++-
.../groovy/editor/java/JavaElementHandle.java | 304 +++++++++++++++++++++
9 files changed, 632 insertions(+), 50 deletions(-)
diff --git a/groovy/groovy.editor/nbproject/project.xml
b/groovy/groovy.editor/nbproject/project.xml
index 0cdabe9..d971f65 100644
--- a/groovy/groovy.editor/nbproject/project.xml
+++ b/groovy/groovy.editor/nbproject/project.xml
@@ -92,7 +92,7 @@
<compile-dependency/>
<run-dependency>
<release-version>2</release-version>
- <specification-version>2.21</specification-version>
+ <specification-version>2.43</specification-version>
</run-dependency>
</dependency>
<dependency>
diff --git
a/groovy/groovy.editor/src/org/netbeans/modules/groovy/editor/api/completion/CompletionHandler.java
b/groovy/groovy.editor/src/org/netbeans/modules/groovy/editor/api/completion/CompletionHandler.java
index 79812ed..30c574f 100644
---
a/groovy/groovy.editor/src/org/netbeans/modules/groovy/editor/api/completion/CompletionHandler.java
+++
b/groovy/groovy.editor/src/org/netbeans/modules/groovy/editor/api/completion/CompletionHandler.java
@@ -22,12 +22,18 @@ import groovy.lang.MetaMethod;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
+import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.*;
+import java.util.concurrent.Callable;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
import java.util.logging.Level;
import java.util.logging.Logger;
+import javax.lang.model.element.Element;
import javax.swing.text.Document;
import javax.swing.text.JTextComponent;
import org.codehaus.groovy.ast.ASTNode;
@@ -36,6 +42,8 @@ import org.codehaus.groovy.ast.expr.ArgumentListExpression;
import org.codehaus.groovy.reflection.CachedClass;
import org.netbeans.api.java.platform.JavaPlatform;
import org.netbeans.api.java.platform.JavaPlatformManager;
+import org.netbeans.api.java.source.CompilationInfo;
+import org.netbeans.api.java.source.ui.ElementJavadoc;
import org.netbeans.api.lexer.Token;
import org.netbeans.api.lexer.TokenSequence;
import org.netbeans.editor.BaseDocument;
@@ -53,12 +61,14 @@ import
org.netbeans.modules.groovy.editor.api.lexer.GroovyTokenId;
import org.netbeans.modules.groovy.editor.api.lexer.LexUtilities;
import org.netbeans.modules.groovy.editor.utils.GroovyUtils;
import
org.netbeans.modules.groovy.editor.api.completion.util.CompletionContext;
+import org.netbeans.modules.groovy.editor.java.JavaElementHandle;
import org.netbeans.modules.groovy.support.api.GroovySettings;
+import org.openide.util.Exceptions;
import org.openide.util.NbBundle;
import org.openide.util.Utilities;
import org.openide.util.WeakListeners;
-public class CompletionHandler implements CodeCompletionHandler {
+public class CompletionHandler implements CodeCompletionHandler2 {
private static final Logger LOG =
Logger.getLogger(CompletionHandler.class.getName());
private final PropertyChangeListener docListener;
@@ -433,7 +443,7 @@ public class CompletionHandler implements
CodeCompletionHandler {
String error = NbBundle.getMessage(CompletionHandler.class,
"GroovyCompletion_NoJavaDocFound");
String doctext = null;
-
+
if (element instanceof ASTMethod) {
ASTMethod ame = (ASTMethod) element;
@@ -630,4 +640,43 @@ public class CompletionHandler implements
CodeCompletionHandler {
}
return ParameterInfo.NONE;
}
+
+ @Override
+ public Documentation documentElement(ParserResult info, ElementHandle
handle, Callable<Boolean> cancel) {
+ if (handle instanceof JavaElementHandle) {
+ // let Java support do the hard work.
+ ElementJavadoc jdoc;
+ try {
+ jdoc = ((JavaElementHandle)handle).extract(info, new
JavaElementHandle.ElementFunction<ElementJavadoc>() {
+ @Override
+ public ElementJavadoc apply(CompilationInfo info, Element
el) {
+ return ElementJavadoc.create(info, el);
+ }
+ });
+ } catch (IOException ex) {
+ // TBR
+ return null;
+ }
+
+ if (jdoc != null) {
+ Boolean b;
+ Future<String> content = jdoc.getTextAsync();
+ try {
+ while (((b = cancel.call()) == null) || !b.booleanValue())
{
+ try {
+ return Documentation.create(content.get(250,
TimeUnit.MILLISECONDS),
+ jdoc.getURL());
+ } catch (TimeoutException te) {}
+ }
+ } catch (Exception ex) {
+ Exceptions.printStackTrace(ex);
+ return null;
+ }
+ return null;
+ }
+ }
+
+ String s = document(info, handle);
+ return s == null ? null : Documentation.create(s);
+ }
}
diff --git
a/groovy/groovy.editor/src/org/netbeans/modules/groovy/editor/api/completion/CompletionItem.java
b/groovy/groovy.editor/src/org/netbeans/modules/groovy/editor/api/completion/CompletionItem.java
index e04b1f3..81b955f 100644
---
a/groovy/groovy.editor/src/org/netbeans/modules/groovy/editor/api/completion/CompletionItem.java
+++
b/groovy/groovy.editor/src/org/netbeans/modules/groovy/editor/api/completion/CompletionItem.java
@@ -42,6 +42,8 @@ import
org.netbeans.modules.groovy.editor.api.elements.ElementHandleSupport;
import org.netbeans.modules.groovy.editor.api.elements.GroovyElement;
import org.netbeans.modules.groovy.editor.api.elements.KeywordElement;
import
org.netbeans.modules.groovy.editor.api.elements.common.MethodElement.MethodParameter;
+import
org.netbeans.modules.groovy.editor.completion.provider.CompletionAccessor;
+import org.netbeans.modules.groovy.editor.java.JavaElementHandle;
import org.netbeans.modules.groovy.editor.java.Utilities;
import org.netbeans.modules.groovy.editor.utils.GroovyUtils;
import org.netbeans.modules.groovy.support.api.GroovySources;
@@ -61,8 +63,47 @@ public abstract class CompletionItem extends
DefaultCompletionProposal {
private static volatile ImageIcon groovyIcon;
private static volatile ImageIcon javaIcon;
private static volatile ImageIcon newConstructorIcon;
-
+ static {
+ CompletionAccessor.setInstance(new CompletionAccessor() {
+ @Override
+ public CompletionItem assignHandle(CompletionItem item,
JavaElementHandle jh) {
+ synchronized (item) {
+ if (item instanceof ConstructorItem) {
+ ((ConstructorItem)item).assignHandle(jh);
+ } else if (item instanceof JavaFieldItem) {
+ ((JavaFieldItem)item).assignHandle(jh);
+ } else if (item instanceof JavaMethodItem) {
+ ((JavaMethodItem)item).assignHandle(jh);
+ } else if (item instanceof TypeItem) {
+ ((TypeItem)item).assignHandle(jh);
+ } else {
+ throw new IllegalArgumentException(item.getClass() +
":" + item.toString());
+ }
+ }
+ return item;
+ }
+
+ @Override
+ public ConstructorItem createConstructor(JavaElementHandle h,
List<MethodParameter> parameters, int anchorOffset, boolean expand) {
+ ConstructorItem ci = new ConstructorItem(h.getIn(),
h.getName(), parameters, anchorOffset, expand);
+ synchronized (ci) {
+ ci.handle = h;
+ }
+ return ci;
+ }
+
+ @Override
+ public TypeItem createType(JavaElementHandle h, String qn, String
n, int anchorOffset, javax.lang.model.element.ElementKind ek) {
+ TypeItem ti = new TypeItem(qn, n, anchorOffset, ek);
+ synchronized (ti) {
+ ti.handle = h;
+ }
+ return ti;
+ }
+ });
+ }
+
private CompletionItem(GroovyElement element, int anchorOffset) {
this.element = element;
this.anchorOffset = anchorOffset;
@@ -145,7 +186,7 @@ public abstract class CompletionItem extends
DefaultCompletionProposal {
public static CompletionItem forDynamicField(int anchorOffset, String
name, String type) {
return new DynamicFieldItem(anchorOffset, name, type);
}
-
+
private static class JavaMethodItem extends CompletionItem {
private final String className;
@@ -155,7 +196,8 @@ public abstract class CompletionItem extends
DefaultCompletionProposal {
private final Set<javax.lang.model.element.Modifier> modifiers;
private final boolean emphasise;
private final boolean nameOnly;
-
+ private ElementHandle handle;
+
public JavaMethodItem(String className, String simpleName,
List<String> parameters, TypeMirror returnType,
Set<javax.lang.model.element.Modifier> modifiers, int
anchorOffset, boolean emphasise, boolean nameOnly) {
@@ -233,7 +275,12 @@ public abstract class CompletionItem extends
DefaultCompletionProposal {
@Override
public ElementHandle getElement() {
- return ElementHandleSupport.createHandle(className, simpleName,
ElementKind.METHOD, getModifiers());
+ synchronized (this) {
+ if (handle == null) {
+ handle = ElementHandleSupport.createHandle(className,
simpleName, ElementKind.METHOD, getModifiers());
+ }
+ return handle;
+ }
}
@Override
@@ -244,6 +291,9 @@ public abstract class CompletionItem extends
DefaultCompletionProposal {
return super.getCustomInsertTemplate();
}
+ public void assignHandle(ElementHandle h) {
+ this.handle = h;
+ }
}
public static class DynamicFieldItem extends CompletionItem {
@@ -637,6 +687,7 @@ public abstract class CompletionItem extends
DefaultCompletionProposal {
private final String fqn;
private final String name;
private final javax.lang.model.element.ElementKind ek;
+ private ElementHandle handle;
public TypeItem(String fqn, String name, int anchorOffset,
javax.lang.model.element.ElementKind ek) {
super(null, anchorOffset);
@@ -668,32 +719,49 @@ public abstract class CompletionItem extends
DefaultCompletionProposal {
public Set<Modifier> getModifiers() {
return Collections.emptySet();
}
+
+ void assignHandle(ElementHandle h) {
+ this.handle = h;
+ }
@Override
public ElementHandle getElement() {
- // For completion documentation
- // return ElementHandleSupport.createHandle(request.info, new
ClassElement(name));
- return null;
+ synchronized (this) {
+ if (handle == null) {
+ handle = ElementHandleSupport.createHandle(fqn, name,
elementKind, Collections.emptySet());
+ }
+ return handle;
+ }
}
}
-
+
public static class ConstructorItem extends CompletionItem {
private static final String NEW_CSTR =
"org/netbeans/modules/groovy/editor/resources/new_constructor_16.png"; //NOI18N
private final boolean expand; // should this item expand to a
constructor body?
private final String name;
+ private final String className;
private final String paramListString;
private final List<MethodParameter> parameters;
+ private ElementHandle handle;
public ConstructorItem(String name, List<MethodParameter> parameters,
int anchorOffset, boolean expand) {
+ this(null, name, parameters, anchorOffset, expand);
+ }
+
+ /**
+ * Package private; use {@link CompletionAccessor} to make instances
of this.
+ */
+ ConstructorItem(String clazzName, String name, List<MethodParameter>
parameters, int anchorOffset, boolean expand) {
super(null, anchorOffset);
+ this.className = clazzName;
this.name = name;
this.expand = expand;
this.parameters = parameters;
this.paramListString = parseParams();
}
-
+
private String parseParams() {
StringBuilder sb = new StringBuilder();
if (!parameters.isEmpty()) {
@@ -746,9 +814,16 @@ public abstract class CompletionItem extends
DefaultCompletionProposal {
@Override
public ElementHandle getElement() {
- // For completion documentation
- // return ElementHandleSupport.createHandle(request.info, new
ClassElement(name));
- return null;
+ synchronized (this) {
+ if (handle == null) {
+ handle = ElementHandleSupport.createHandle(className,
name, ElementKind.CONSTRUCTOR, getModifiers());
+ }
+ return handle;
+ }
+ }
+
+ void assignHandle(ElementHandle h) {
+ this.handle = handle;
}
// Constructors are smart by definition (have to be place above others)
@@ -840,7 +915,7 @@ public abstract class CompletionItem extends
DefaultCompletionProposal {
return hash;
}
}
-
+
public static class NamedParameter extends CompletionItem {
private final String typeName;
@@ -917,6 +992,7 @@ public abstract class CompletionItem extends
DefaultCompletionProposal {
private final TypeMirror type;
private final Set<javax.lang.model.element.Modifier> modifiers;
private final boolean emphasise;
+ private ElementHandle handle;
public JavaFieldItem(String className, String name, TypeMirror type,
@@ -961,10 +1037,19 @@ public abstract class CompletionItem extends
DefaultCompletionProposal {
public Set<Modifier> getModifiers() {
return Utilities.modelModifiersToGsf(modifiers);
}
+
+ void assignHandle(ElementHandle h) {
+ this.handle = h;
+ }
@Override
public ElementHandle getElement() {
- return ElementHandleSupport.createHandle(className, name,
ElementKind.FIELD, getModifiers());
+ synchronized (this) {
+ if (handle == null) {
+ handle = ElementHandleSupport.createHandle(className,
name, ElementKind.FIELD, getModifiers());
+ }
+ return handle;
+ }
}
}
diff --git
a/groovy/groovy.editor/src/org/netbeans/modules/groovy/editor/completion/MethodCompletion.java
b/groovy/groovy.editor/src/org/netbeans/modules/groovy/editor/completion/MethodCompletion.java
index b95cfe2..71a5274 100644
---
a/groovy/groovy.editor/src/org/netbeans/modules/groovy/editor/completion/MethodCompletion.java
+++
b/groovy/groovy.editor/src/org/netbeans/modules/groovy/editor/completion/MethodCompletion.java
@@ -31,6 +31,7 @@ import org.netbeans.api.annotations.common.NonNull;
import org.netbeans.api.java.classpath.ClassPath;
import org.netbeans.api.java.source.ClasspathInfo;
import org.netbeans.api.java.source.CompilationController;
+import org.netbeans.api.java.source.ElementHandle;
import org.netbeans.api.java.source.JavaSource;
import org.netbeans.api.java.source.Task;
import org.netbeans.modules.csl.api.CompletionProposal;
@@ -45,10 +46,12 @@ import org.netbeans.modules.groovy.editor.utils.GroovyUtils;
import
org.netbeans.modules.groovy.editor.api.completion.util.CompletionContext;
import
org.netbeans.modules.groovy.editor.api.elements.common.MethodElement.MethodParameter;
import org.netbeans.modules.groovy.editor.api.elements.index.IndexedClass;
-import org.netbeans.modules.groovy.editor.api.elements.index.IndexedField;
import org.netbeans.modules.groovy.editor.api.elements.index.IndexedMethod;
+import
org.netbeans.modules.groovy.editor.completion.provider.CompletionAccessor;
import org.netbeans.modules.groovy.editor.imports.ImportUtils;
+import org.netbeans.modules.groovy.editor.java.JavaElementHandle;
import org.netbeans.modules.parsing.spi.indexing.support.QuerySupport;
+import org.openide.filesystems.FileObject;
/**
* Complete the methods invokable on a class.
@@ -162,11 +165,11 @@ public class MethodCompletion extends BaseCompletion {
typelist.addAll(getElementListFor(info.getElements(), importName));
}
LOG.log(Level.FINEST, "Number of types found: {0}",
typelist.size());
-
+
if (exactConstructorExists(typelist,
context.getPrefix())) {
// if we are in situation like "String s = new
String|" we want to
// show only String constructors (not StringBuffer
constructors etc.)
- addExactProposals(typelist);
+ addExactProposals(context.getSourceFile(),
typelist);
}
addConstructorProposalsForDeclaredClasses();
}
@@ -280,10 +283,11 @@ public class MethodCompletion extends BaseCompletion {
return false;
}
- private void addExactProposals(List<? extends Element> typelist) {
+ private void addExactProposals(FileObject source, List<? extends Element>
typelist) {
for (Element element : typelist) {
// only look for classes rather than enums or interfaces
if (element.getKind() == ElementKind.CLASS) {
+ TypeElement tel = (TypeElement)element;
for (Element encl : element.getEnclosedElements()) {
if (encl.getKind() == ElementKind.CONSTRUCTOR) {
// we gotta get the constructors name from the type
itself, since
@@ -291,7 +295,7 @@ public class MethodCompletion extends BaseCompletion {
String constructorName =
element.getSimpleName().toString();
if
(constructorName.toUpperCase().equals(context.getPrefix().toUpperCase())) {
- addConstructorProposal(constructorName,
(ExecutableElement) encl);
+
addConstructorProposal(tel.getQualifiedName().toString(), (ExecutableElement)
encl);
}
}
}
@@ -299,10 +303,15 @@ public class MethodCompletion extends BaseCompletion {
}
}
- private void addConstructorProposal(String constructorName,
ExecutableElement encl) {
+ private void addConstructorProposal(String classFqn, ExecutableElement
encl) {
List<MethodParameter> paramList = getParameterList(encl);
-
- ConstructorItem constructor = new ConstructorItem(constructorName,
paramList, anchor, false);
+ List<String> sig = new ArrayList<>(paramList.size());
+ for (MethodParameter p : paramList) {
+ sig.add(p.getFqnType());
+ }
+ JavaElementHandle h = new
JavaElementHandle(encl.getEnclosingElement().getSimpleName().toString(),
classFqn, ElementHandle.create(encl), sig, Collections.emptySet());
+ CompletionItem constructor = CompletionAccessor.instance().
+ createConstructor(h, paramList, anchor, false);
if (!proposals.contains(constructor)) {
proposals.add(constructor);
}
diff --git
a/groovy/groovy.editor/src/org/netbeans/modules/groovy/editor/completion/TypesCompletion.java
b/groovy/groovy.editor/src/org/netbeans/modules/groovy/editor/completion/TypesCompletion.java
index f8568e9..acc9f98 100644
---
a/groovy/groovy.editor/src/org/netbeans/modules/groovy/editor/completion/TypesCompletion.java
+++
b/groovy/groovy.editor/src/org/netbeans/modules/groovy/editor/completion/TypesCompletion.java
@@ -47,7 +47,9 @@ import
org.netbeans.modules.groovy.editor.api.elements.index.IndexedClass;
import org.netbeans.modules.groovy.editor.api.lexer.GroovyTokenId;
import org.netbeans.modules.groovy.editor.utils.GroovyUtils;
import
org.netbeans.modules.groovy.editor.api.completion.util.CompletionContext;
+import
org.netbeans.modules.groovy.editor.completion.provider.CompletionAccessor;
import org.netbeans.modules.groovy.editor.imports.ImportUtils;
+import org.netbeans.modules.groovy.editor.java.JavaElementHandle;
import org.netbeans.modules.parsing.spi.indexing.support.QuerySupport;
import org.openide.filesystems.FileObject;
@@ -313,14 +315,18 @@ public class TypesCompletion extends BaseCompletion {
}
// We are dealing with prefix for some class type
+ JavaElementHandle jh = null;
+ if (type.getHandle() != null) {
+ jh = new JavaElementHandle(fqnTypeName, typeName,
type.getHandle(), Collections.emptyList(), Collections.emptySet());
+ }
if (isPrefixed(request, typeName)) {
alreadyPresent.add(type);
- proposals.add(new CompletionItem.TypeItem(fqnTypeName, typeName,
anchor, type.getKind()));
+ proposals.add(CompletionAccessor.instance().createType(jh,
fqnTypeName, typeName, anchor, type.getKind()));
}
// We are dealing with CamelCase completion for some class type
if (CamelCaseUtil.compareCamelCase(typeName, request.getPrefix())) {
- CompletionItem.TypeItem camelCaseProposal = new
CompletionItem.TypeItem(fqnTypeName, typeName, anchor, ElementKind.CLASS);
+ CompletionItem.TypeItem camelCaseProposal =
CompletionAccessor.instance().createType(jh, fqnTypeName, typeName, anchor,
ElementKind.CLASS);
if (!proposals.contains(camelCaseProposal)) {
proposals.add(camelCaseProposal);
@@ -357,7 +363,7 @@ public class TypesCompletion extends BaseCompletion {
|| samePackage &&
(modifiers.contains(Modifier.PROTECTED)
|| (!modifiers.contains(Modifier.PUBLIC)
&& !modifiers.contains(Modifier.PRIVATE)))) {
- result.add(new
TypeHolder(element.toString(), element.getKind()));
+ result.add(new
TypeHolder(element.toString(),
org.netbeans.api.java.source.ElementHandle.create(element)));
}
}
}
@@ -374,7 +380,7 @@ public class TypesCompletion extends BaseCompletion {
|| samePackage &&
(modifiers.contains(Modifier.PROTECTED)
|| (!modifiers.contains(Modifier.PUBLIC)
&& !modifiers.contains(Modifier.PRIVATE)))) {
- result.add(new
TypeHolder(element.toString(), element.getKind()));
+ result.add(new
TypeHolder(element.toString(),
org.netbeans.api.java.source.ElementHandle.create(element)));
}
}
}
@@ -392,6 +398,7 @@ public class TypesCompletion extends BaseCompletion {
private final String name;
private final ElementKind kind;
+ private final org.netbeans.api.java.source.ElementHandle handle;
public TypeHolder(IndexedClass indexedClass) {
this.name = indexedClass.getFqn();
@@ -401,11 +408,23 @@ public class TypesCompletion extends BaseCompletion {
} else {
this.kind = ElementKind.INTERFACE;
}
+ this.handle = null;
}
public TypeHolder(String name, ElementKind kind) {
this.name = name;
this.kind = kind;
+ this.handle = null;
+ }
+
+ public TypeHolder(String name,
org.netbeans.api.java.source.ElementHandle handle) {
+ this.name = name;
+ this.handle = handle;
+ this.kind = handle.getKind();
+ }
+
+ public org.netbeans.api.java.source.ElementHandle getHandle() {
+ return handle;
}
public ElementKind getKind() {
diff --git
a/groovy/groovy.editor/src/org/netbeans/modules/groovy/editor/completion/provider/CompletionAccessor.java
b/groovy/groovy.editor/src/org/netbeans/modules/groovy/editor/completion/provider/CompletionAccessor.java
new file mode 100644
index 0000000..1e79a57
--- /dev/null
+++
b/groovy/groovy.editor/src/org/netbeans/modules/groovy/editor/completion/provider/CompletionAccessor.java
@@ -0,0 +1,64 @@
+/*
+ * 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.groovy.editor.completion.provider;
+
+import java.util.List;
+import javax.lang.model.element.ElementKind;
+import org.netbeans.modules.groovy.editor.api.completion.CompletionItem;
+import
org.netbeans.modules.groovy.editor.api.completion.CompletionItem.ConstructorItem;
+import
org.netbeans.modules.groovy.editor.api.completion.CompletionItem.TypeItem;
+import org.netbeans.modules.groovy.editor.api.elements.common.MethodElement;
+import org.netbeans.modules.groovy.editor.java.JavaElementHandle;
+
+/**
+ * Avoids publishing additional API methods; the API needs a thorough review,
I am not
+ * capable of at the moment.
+ *
+ * @author sdedic
+ */
+public abstract class CompletionAccessor {
+ private static CompletionAccessor INSTANCE;
+
+ static {
+ new TypeItem("", "", 0, ElementKind.CLASS);
+ }
+
+ public static void setInstance(CompletionAccessor acc) {
+ synchronized (CompletionAccessor.class) {
+ if (INSTANCE != null && INSTANCE != acc) {
+ throw new IllegalStateException();
+ }
+ INSTANCE = acc;
+ }
+ }
+
+ public static CompletionAccessor instance() {
+ return INSTANCE;
+ }
+
+ /**
+ * Assigns a Handle to a java element item. The passed item must be an
instance of JavaElementItem
+ * Created by forMethod() or forField() factories.
+ */
+ public abstract CompletionItem assignHandle(CompletionItem item,
JavaElementHandle jh);
+
+ public abstract ConstructorItem createConstructor(JavaElementHandle h,
List<MethodElement.MethodParameter> parameters, int anchorOffset, boolean
expand);
+
+ public abstract TypeItem createType(JavaElementHandle h, String qn, String
n, int anchorOffset, javax.lang.model.element.ElementKind ek);
+}
diff --git
a/groovy/groovy.editor/src/org/netbeans/modules/groovy/editor/completion/provider/GroovyElementsProvider.java
b/groovy/groovy.editor/src/org/netbeans/modules/groovy/editor/completion/provider/GroovyElementsProvider.java
index 1853c5d..b7af058 100644
---
a/groovy/groovy.editor/src/org/netbeans/modules/groovy/editor/completion/provider/GroovyElementsProvider.java
+++
b/groovy/groovy.editor/src/org/netbeans/modules/groovy/editor/completion/provider/GroovyElementsProvider.java
@@ -19,12 +19,14 @@
package org.netbeans.modules.groovy.editor.completion.provider;
+import java.net.URL;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import javax.lang.model.element.Modifier;
import org.netbeans.api.java.classpath.ClassPath;
+import org.netbeans.modules.csl.api.ElementKind;
import org.netbeans.modules.groovy.editor.api.GroovyIndex;
import org.netbeans.modules.groovy.editor.api.completion.CompletionItem;
import org.netbeans.modules.groovy.editor.api.completion.FieldSignature;
@@ -35,9 +37,11 @@ import
org.netbeans.modules.groovy.editor.api.elements.index.IndexedMethod;
import org.netbeans.modules.groovy.editor.completion.AccessLevel;
import org.netbeans.modules.groovy.editor.java.Utilities;
import
org.netbeans.modules.groovy.editor.api.completion.util.CompletionContext;
+import org.netbeans.modules.groovy.editor.java.JavaElementHandle;
import org.netbeans.modules.groovy.editor.spi.completion.CompletionProvider;
import org.netbeans.modules.parsing.spi.indexing.support.QuerySupport;
import org.openide.filesystems.FileObject;
+import org.openide.filesystems.URLMapper;
/**
*
@@ -62,15 +66,28 @@ public final class GroovyElementsProvider implements
CompletionProvider {
for (IndexedMethod indexedMethod : methods) {
if (accept(context.access, indexedMethod)) {
- result.put(getMethodSignature(indexedMethod),
CompletionItem.forJavaMethod(
- context.getTypeName(),
- indexedMethod.getName(),
- indexedMethod.getParameterTypes(),
- indexedMethod.getReturnType(),
-
Utilities.gsfModifiersToModel(indexedMethod.getModifiers(), Modifier.PUBLIC),
- context.getAnchor(),
- false,
- context.isNameOnly()));
+ JavaElementHandle jeh = null;
+ if
(indexedMethod.getFileObject().getMIMEType("text/x-java") != null) {
+ URL u =
URLMapper.findURL(indexedMethod.getFileObject(), URLMapper.INTERNAL);
+ jeh = new JavaElementHandle(u,
+ indexedMethod.getName(),
context.getTypeName(), ElementKind.METHOD,
+ indexedMethod.getParameterTypes(),
+ indexedMethod.getModifiers());
+ }
+
+ CompletionItem ci = CompletionItem.forJavaMethod(
+ context.getTypeName(),
+ indexedMethod.getName(),
+ indexedMethod.getParameterTypes(),
+ indexedMethod.getReturnType(),
+
Utilities.gsfModifiersToModel(indexedMethod.getModifiers(), Modifier.PUBLIC),
+ context.getAnchor(),
+ false,
+ context.isNameOnly());
+
+ result.put(getMethodSignature(indexedMethod),
+ CompletionAccessor.instance().assignHandle(ci, jeh)
+ );
}
}
}
diff --git
a/groovy/groovy.editor/src/org/netbeans/modules/groovy/editor/completion/provider/JavaElementHandler.java
b/groovy/groovy.editor/src/org/netbeans/modules/groovy/editor/completion/provider/JavaElementHandler.java
index 4d70d13..4979ef9 100644
---
a/groovy/groovy.editor/src/org/netbeans/modules/groovy/editor/completion/provider/JavaElementHandler.java
+++
b/groovy/groovy.editor/src/org/netbeans/modules/groovy/editor/completion/provider/JavaElementHandler.java
@@ -44,15 +44,19 @@ import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import org.netbeans.api.java.source.ClasspathInfo;
import org.netbeans.api.java.source.CompilationController;
+import org.netbeans.api.java.source.CompilationInfo;
+import org.netbeans.api.java.source.ElementHandle;
import org.netbeans.api.java.source.ElementUtilities.ElementAcceptor;
import org.netbeans.api.java.source.JavaSource;
import org.netbeans.api.java.source.Task;
+import org.netbeans.api.java.source.TypeUtilities;
import org.netbeans.modules.csl.spi.ParserResult;
import org.netbeans.modules.groovy.editor.utils.GroovyUtils;
-import org.netbeans.modules.groovy.editor.api.completion.CompletionHandler;
import org.netbeans.modules.groovy.editor.api.completion.FieldSignature;
import org.netbeans.modules.groovy.editor.api.completion.MethodSignature;
import org.netbeans.modules.groovy.editor.completion.AccessLevel;
+import org.netbeans.modules.groovy.editor.java.JavaElementHandle;
+import org.netbeans.modules.groovy.editor.java.Utilities;
import org.openide.filesystems.FileObject;
/**
@@ -78,16 +82,18 @@ public final class JavaElementHandler {
public Map<MethodSignature, CompletionItem> getMethods(String className,
String prefix, int anchor, String[] typeParameters, boolean
emphasise, Set<AccessLevel> levels, boolean nameOnly) {
JavaSource javaSource = createJavaSource();
-
+
if (javaSource == null) {
return Collections.emptyMap();
}
+
+ FileObject f = info.getSnapshot().getSource().getFileObject();
CountDownLatch cnt = new CountDownLatch(1);
Map<MethodSignature, CompletionItem> result =
Collections.synchronizedMap(new HashMap<MethodSignature, CompletionItem>());
try {
- javaSource.runUserActionTask(new MethodCompletionHelper(cnt,
javaSource, className, typeParameters,
+ javaSource.runUserActionTask(new MethodCompletionHelper(cnt,
javaSource, f, className, typeParameters,
levels, prefix, anchor, result, emphasise, nameOnly),
true);
} catch (IOException ex) {
LOG.log(Level.FINEST, "Problem in runUserActionTask : {0}",
ex.getMessage());
@@ -114,8 +120,9 @@ public final class JavaElementHandler {
CountDownLatch cnt = new CountDownLatch(1);
Map<FieldSignature, CompletionItem> result =
Collections.synchronizedMap(new HashMap<FieldSignature, CompletionItem>());
+ FileObject f = info.getSnapshot().getSource().getFileObject();
try {
- javaSource.runUserActionTask(new FieldCompletionHelper(cnt,
javaSource, className,
+ javaSource.runUserActionTask(new FieldCompletionHelper(cnt,
javaSource, f, className,
Collections.singleton(AccessLevel.PUBLIC), prefix, anchor,
result, emphasise), true);
} catch (IOException ex) {
LOG.log(Level.FINEST, "Problem in runUserActionTask : {0}",
ex.getMessage());
@@ -154,6 +161,8 @@ public final class JavaElementHandler {
private final JavaSource javaSource;
+ private final FileObject groovySource;
+
private final String className;
private final String[] typeParameters;
@@ -170,12 +179,13 @@ public final class JavaElementHandler {
private final boolean nameOnly;
- public MethodCompletionHelper(CountDownLatch cnt, JavaSource
javaSource, String className,
+ public MethodCompletionHelper(CountDownLatch cnt, JavaSource
javaSource, FileObject groovySource, String className,
String[] typeParameters, Set<AccessLevel> levels, String
prefix, int anchor,
Map<MethodSignature, CompletionItem> proposals, boolean
emphasise, boolean nameOnly) {
this.cnt = cnt;
this.javaSource = javaSource;
+ this.groovySource = groovySource;
this.className = className;
this.typeParameters = typeParameters;
this.levels = levels;
@@ -219,8 +229,15 @@ public final class JavaElementHandler {
if
(simpleName.toUpperCase(Locale.ENGLISH).startsWith(prefix.toUpperCase(Locale.ENGLISH))
&&
!simpleName.contains("$")) {
- proposals.put(getSignature(te, element,
typeParameters, info.getTypes()), CompletionItem.forJavaMethod(
- className, simpleName, params, returnType,
element.getModifiers(), anchor, emphasise, nameOnly));
+ JavaElementHandle h = new JavaElementHandle(
+ simpleName, className,
ElementHandle.create(element),
+ signatureOf(info, element),
Utilities.modelModifiersToGsf(element.getModifiers()));
+
+ CompletionItem ci = CompletionItem.forJavaMethod(
+ className, simpleName, params,
returnType, element.getModifiers(), anchor, emphasise, nameOnly);
+ proposals.put(getSignature(te, element,
typeParameters, info.getTypes()),
+ CompletionAccessor.instance().assignHandle(ci,
h)
+ );
}
}
}
@@ -228,6 +245,15 @@ public final class JavaElementHandler {
cnt.countDown();
}
+ private List<String> signatureOf(CompilationInfo info,
ExecutableElement exe) {
+ List<String> fqns = new ArrayList<>(exe.getParameters().size());
+ for (VariableElement v : exe.getParameters()) {
+ fqns.add(info.getTypeUtilities().getTypeName(v.asType(),
TypeUtilities.TypeNameOptions.PRINT_FQN).
+ toString());
+ }
+ return fqns;
+ }
+
private List<String> getParameterListForMethod(ExecutableElement exe) {
List<String> parameters = new ArrayList<String>();
@@ -312,13 +338,16 @@ public final class JavaElementHandler {
private final boolean emphasise;
private final Map<FieldSignature, CompletionItem> proposals;
-
- public FieldCompletionHelper(CountDownLatch cnt, JavaSource
javaSource, String className,
+
+ private final FileObject groovySource;
+
+ public FieldCompletionHelper(CountDownLatch cnt, JavaSource
javaSource, FileObject groovySource, String className,
Set<AccessLevel> levels, String prefix, int anchor,
Map<FieldSignature, CompletionItem> proposals, boolean
emphasise) {
this.cnt = cnt;
this.javaSource = javaSource;
+ this.groovySource = groovySource;
this.className = className;
this.levels = levels;
this.prefix = prefix;
@@ -360,9 +389,15 @@ public final class JavaElementHandler {
if (LOG.isLoggable(Level.FINEST)) {
LOG.log(Level.FINEST, simpleName + " " +
type.toString());
}
-
- proposals.put(getSignature(te, element), new
CompletionItem.JavaFieldItem(
- className, simpleName, type,
element.getModifiers(), anchor, emphasise));
+
+ JavaElementHandle jh = new JavaElementHandle(
+ simpleName, className,
ElementHandle.create(element), null,
+
Utilities.modelModifiersToGsf(element.getModifiers()));
+
+ CompletionItem ci = new
CompletionItem.JavaFieldItem(
+ className, simpleName, type,
element.getModifiers(), anchor, emphasise);
+ proposals.put(getSignature(te, element),
+ CompletionAccessor.instance().assignHandle(ci,
jh));
}
}
}
diff --git
a/groovy/groovy.editor/src/org/netbeans/modules/groovy/editor/java/JavaElementHandle.java
b/groovy/groovy.editor/src/org/netbeans/modules/groovy/editor/java/JavaElementHandle.java
new file mode 100644
index 0000000..8a765c9
--- /dev/null
+++
b/groovy/groovy.editor/src/org/netbeans/modules/groovy/editor/java/JavaElementHandle.java
@@ -0,0 +1,304 @@
+/*
+ * 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.groovy.editor.java;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.ElementFilter;
+import org.netbeans.api.annotations.common.CheckForNull;
+import org.netbeans.api.annotations.common.NonNull;
+import org.netbeans.api.annotations.common.NullAllowed;
+import org.netbeans.api.java.source.ClasspathInfo;
+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.Task;
+import org.netbeans.api.java.source.TypeUtilities;
+import org.netbeans.modules.csl.api.ElementHandle;
+import org.netbeans.modules.csl.api.ElementKind;
+import org.netbeans.modules.csl.api.Modifier;
+import org.netbeans.modules.csl.api.OffsetRange;
+import org.netbeans.modules.csl.spi.ParserResult;
+import org.netbeans.modules.groovy.editor.api.elements.index.IndexedElement;
+import org.openide.filesystems.FileObject;
+import org.openide.filesystems.URLMapper;
+
+/**
+ * Handle that represent an {@link IndexedElement}. But as the {@link
IndexedElement} is stateful
+ * ant may keep the Document instance, this just uses the qualified name to
identify the
+ * real element.
+ * <p>
+ * Some items are created from Groovy indexes, so there's no real Element to
create ElementHandle from,
+ * so if an ElementHandle is available, the JavaElementHandle should be
created on top of the real
+ * java source APIs ElementHandle. Otherwise, the coordinates are computed
from URL, qualified parent, simple
+ * name kind and signature types.
+ *
+ * @author sdedic
+ */
+public final class JavaElementHandle implements ElementHandle {
+ /**
+ * URL of the file where the handle was created; used to construct
classpaths etc.
+ */
+ private final URL anchorURL;
+
+ /**
+ * Name of the element
+ */
+ private final String name;
+
+ /**
+ * FQN of the outer/owner element.
+ */
+ private final String ownerFQN;
+
+ /**
+ * Kind of the element
+ */
+ private final ElementKind kind;
+
+ /**
+ * Signature info, for methods and ctors only
+ */
+ private final List<String> signatureInfo;
+
+ private final Set<Modifier> modifiers;
+
+ private final org.netbeans.api.java.source.ElementHandle elementHandle;
+
+ public JavaElementHandle(String name, String ownerFQN,
org.netbeans.api.java.source.ElementHandle h, List<String> signatureInfo,
Set<Modifier> modifiers) {
+ assert name != null : "simple name is needed";
+ this.anchorURL = null;
+ this.name = name;
+ this.ownerFQN = ownerFQN;
+ this.elementHandle = h;
+ this.kind = fromJavaKind(h.getKind());
+ this.signatureInfo = signatureInfo == null ? Collections.emptyList() :
signatureInfo;
+ this.modifiers = modifiers == null ? Collections.emptySet() :
modifiers;
+ }
+
+ private static ElementKind
fromJavaKind(javax.lang.model.element.ElementKind k) {
+ switch (k) {
+ case CLASS: return ElementKind.CLASS;
+ case INTERFACE: return ElementKind.INTERFACE;
+ case METHOD: return ElementKind.METHOD;
+ case FIELD: return ElementKind.FIELD;
+ case CONSTRUCTOR: return ElementKind.CONSTRUCTOR;
+ case ENUM: return ElementKind.CONSTANT;
+ case ANNOTATION_TYPE: return ElementKind.INTERFACE;
+
+ default:
+ throw new IllegalArgumentException("Unsupported: " + k);
+ }
+ }
+
+ public JavaElementHandle(URL anchorURL, String name, String ownerFQN,
ElementKind kind, List<String> signatureInfo, Set<Modifier> modifiers) {
+ assert anchorURL != null : "Need an anchor to resolve the handle in
the future";
+ assert name != null : "simple name is needed";
+ assert kind != null;
+ this.anchorURL = anchorURL;
+ this.name = name;
+ this.ownerFQN = ownerFQN;
+ this.kind = kind;
+ this.signatureInfo = signatureInfo == null ? Collections.emptyList() :
signatureInfo;
+ this.modifiers = modifiers == null ? Collections.emptySet() :
modifiers;
+ this.elementHandle = null;
+ }
+
+ @Override
+ public FileObject getFileObject() {
+ return URLMapper.findFileObject(anchorURL);
+ }
+
+ @Override
+ public String getMimeType() {
+ return "text/x-java"; // NOI18N
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public String getIn() {
+ return ownerFQN;
+ }
+
+ @Override
+ public ElementKind getKind() {
+ return kind;
+ }
+
+ @Override
+ public Set<Modifier> getModifiers() {
+ return modifiers;
+ }
+
+ @Override
+ public boolean signatureEquals(ElementHandle handle) {
+ if (handle instanceof JavaElementHandle) {
+ JavaElementHandle jeh = (JavaElementHandle)handle;
+ if (jeh.elementHandle != null && elementHandle != null) {
+ return elementHandle.signatureEquals(jeh.elementHandle);
+ } else {
+ return Objects.equals(jeh.signatureInfo, signatureInfo);
+ }
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public OffsetRange getOffsetRange(ParserResult result) {
+ // for now, to be replaced by lazy-loaded range from resolved Element.
+ return OffsetRange.NONE;
+ }
+
+ /**
+ * Resolves the handle for the passed CompilationInfo
+ * @param <T> element type
+ * @param info compilation info instance
+ * @return resolved element, or {@code null} if none.
+ */
+ public <T extends Element> @CheckForNull T resolve(@NonNull
CompilationInfo info) {
+ return (T)toElement(info);
+ }
+
+ /**
+ * Extracts data from the element.
+ * @param <T>
+ * @param resolver
+ * @return
+ * @throws IOException
+ */
+ public <T> T extract(ParserResult groovyResult, ElementFunction<T>
resolver) throws IOException {
+ FileObject origin =
groovyResult.getSnapshot().getSource().getFileObject();
+ if (origin == null) {
+ if (anchorURL == null) {
+ return null;
+ }
+ origin = URLMapper.findFileObject(anchorURL);
+ }
+ ClasspathInfo cpi = ClasspathInfo.create(origin);
+
+ class Processor implements Task<CompilationController>,
ClasspathInfo.Provider {
+ T result;
+ Exception thrown;
+
+ @Override
+ public void run(CompilationController parameter) throws Exception {
+ try {
+ result = resolver.apply(parameter, toElement(parameter));
+ } catch (Exception ex) {
+ thrown = ex;
+ }
+ }
+
+ @Override
+ public ClasspathInfo getClasspathInfo() {
+ return cpi;
+ }
+ }
+
+ Processor inst = new Processor();
+
+ JavaSource.create(cpi).runUserActionTask(inst, true);
+ if (inst.thrown != null) {
+ throw new IOException(inst.thrown);
+ } else {
+ return inst.result;
+ }
+ }
+
+ private Element toElement(CompilationInfo info) {
+ if (elementHandle != null) {
+ return elementHandle.resolve(info);
+ }
+ switch (kind) {
+ case CLASS:
+ return info.getElements().getTypeElement(name);
+
+ case FIELD:
+ case CONSTANT: {
+ Element owner = info.getElements().getTypeElement(ownerFQN);
+ if (owner == null) {
+ return null;
+ }
+
+ return
ElementFilter.fieldsIn(owner.getEnclosedElements()).stream().
+ filter(f -> f.getSimpleName().contentEquals(name)).
+ findAny().orElse(null);
+ }
+
+ case METHOD:
+ case CONSTRUCTOR: {
+ Element owner = info.getElements().getTypeElement(ownerFQN);
+ if (owner == null || !(owner.getKind().isClass() ||
owner.getKind().isInterface())) {
+ return null;
+ }
+ for (ExecutableElement e : (kind == ElementKind.METHOD ?
ElementFilter.methodsIn(owner.getEnclosedElements()) :
ElementFilter.constructorsIn(owner.getEnclosedElements()))) {
+ if (kind == ElementKind.METHOD &&
!e.getSimpleName().contentEquals(name)) {
+ continue;
+ }
+ List<String> sigTypes = new ArrayList<>();
+ for (VariableElement v : e.getParameters()) {
+ TypeMirror t = v.asType();
+ sigTypes.add(info.getTypeUtilities().getTypeName(t,
TypeUtilities.TypeNameOptions.PRINT_FQN).toString());
+ }
+ if (sigTypes.equals(signatureInfo)) {
+ return e;
+ }
+ }
+ return null;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Processes the ElementHandle into the desired result. The processor's
callback
+ * is called within parser's action task, so it can get a valid {@link
CompilationInfo}
+ * and the resolved {@link Element} instance.
+ * <p>
+ * The implementation must not leak the {@code CompliationInfo} or process
the Element instance
+ * outside of the {@link #apply} call.
+ *
+ * @param <T>
+ */
+ @FunctionalInterface
+ public interface ElementFunction<T> {
+ /**
+ * Produces the desired result from the Element.
+ * @param info compilation info
+ * @param el resolved element; can be {@code null}, if resolution
fails.
+ * @return info extracted from the element.
+ */
+ public @CheckForNull T apply(@NonNull CompilationInfo info,
@NullAllowed Element el);
+ }
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]
For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists