You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@netbeans.apache.org by GitBox <gi...@apache.org> on 2022/05/04 09:04:54 UTC

[GitHub] [netbeans] sdedic commented on a diff in pull request #4071: LSP: Javadoc code completion added.

sdedic commented on code in PR #4071:
URL: https://github.com/apache/netbeans/pull/4071#discussion_r864558327


##########
java/java.editor.base/src/org/netbeans/modules/java/editor/base/javadoc/JavadocCompletionUtils.java:
##########
@@ -337,25 +337,43 @@ public static boolean isInlineTagStart(Token<JavadocTokenId> token) {
     }
     
     private static final Set<DocTree.Kind> BLOCK_TAGS =
-            EnumSet.of(DocTree.Kind.AUTHOR, DocTree.Kind.DEPRECATED, DocTree.Kind.PARAM,
+            EnumSet.of(DocTree.Kind.AUTHOR, DocTree.Kind.DEPRECATED, DocTree.Kind.EXCEPTION,
+                       DocTree.Kind.HIDDEN,DocTree.Kind.PARAM, DocTree.Kind.PROVIDES,
                        DocTree.Kind.RETURN, DocTree.Kind.SEE, DocTree.Kind.SERIAL,
-                       DocTree.Kind.SERIAL_DATA, DocTree.Kind.SERIAL_FIELD, DocTree.Kind.SINCE,
-                       DocTree.Kind.THROWS, DocTree.Kind.UNKNOWN_BLOCK_TAG, DocTree.Kind.VERSION);
+                       DocTree.Kind.SERIAL_DATA, DocTree.Kind.SERIAL_FIELD, DocTree.Kind.SERIAL_FIELD,
+                       DocTree.Kind.SINCE, DocTree.Kind.THROWS, DocTree.Kind.USES, DocTree.Kind.VERSION,
+                       DocTree.Kind.UNKNOWN_BLOCK_TAG);
     public static boolean isBlockTag(DocTreePath tag) {
         return BLOCK_TAGS.contains(normalizedKind(tag.getLeaf()));
     }
 
+    private static final Set<DocTree.Kind> INLINE_TAGS =
+            EnumSet.of(DocTree.Kind.CODE, DocTree.Kind.DOC_ROOT, DocTree.Kind.INDEX,
+                       DocTree.Kind.INHERIT_DOC, DocTree.Kind.LINK, DocTree.Kind.LINK_PLAIN,
+                       DocTree.Kind.LITERAL, DocTree.Kind.SNIPPET, DocTree.Kind.SUMMARY,
+                       DocTree.Kind.SYSTEM_PROPERTY, DocTree.Kind.VALUE, DocTree.Kind.UNKNOWN_INLINE_TAG);
+    public static boolean isInlineTag(DocTreePath tag) {
+        return INLINE_TAGS.contains(normalizedKind(tag.getLeaf()));
+    }
+
     public static DocTree.Kind normalizedKind(DocTree tag) {
         DocTree.Kind normalizedKind = tag.getKind();
         if (normalizedKind == com.sun.source.doctree.DocTree.Kind.ERRONEOUS) {
-            String errorBody = ((ErroneousTree) tag).getBody();
-            switch (errorBody.split("\\s")[0]) {
+            String txt = ((ErroneousTree) tag).getBody().split("\\s")[0];
+            switch (txt) {
                 case "@throws": normalizedKind = DocTree.Kind.THROWS; break;
                 case "@see": normalizedKind = DocTree.Kind.SEE; break;
                 case "@param": normalizedKind = DocTree.Kind.PARAM; break;
                 case "{@value": normalizedKind = DocTree.Kind.VALUE; break;
                 case "{@link": normalizedKind = DocTree.Kind.LINK; break;
                 case "{@linkplain": normalizedKind = DocTree.Kind.LINK; break;
+                default:

Review Comment:
   No normalization for the other tags in `INLINE_TAGS` and `BLOCK_TAGS` ?



##########
java/java.editor/src/org/netbeans/modules/java/editor/javadoc/JavadocCompletionCollector.java:
##########
@@ -0,0 +1,293 @@
+/*
+ * 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.editor.javadoc;
+
+import com.sun.source.tree.Scope;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.Consumer;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.ExecutableType;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+import javax.swing.text.Document;
+import org.netbeans.api.editor.mimelookup.MimeRegistration;
+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.JavaSource;
+import org.netbeans.api.java.source.support.ReferencesCount;
+import org.netbeans.api.lsp.Completion;
+import org.netbeans.api.lsp.TextEdit;
+import org.netbeans.modules.editor.java.JavaCompletionCollector;
+import org.netbeans.modules.editor.java.Utilities;
+import org.netbeans.modules.java.editor.base.javadoc.JavadocCompletionUtils;
+import org.netbeans.modules.parsing.api.ParserManager;
+import org.netbeans.modules.parsing.api.ResultIterator;
+import org.netbeans.modules.parsing.api.Source;
+import org.netbeans.modules.parsing.api.UserTask;
+import org.netbeans.modules.parsing.spi.ParseException;
+import org.netbeans.spi.lsp.CompletionCollector;
+import org.openide.util.Exceptions;
+
+/**
+ *
+ * @author Dusan Balek
+ */
+@MimeRegistration(mimeType = "text/x-java", service = CompletionCollector.class)
+public class JavadocCompletionCollector implements CompletionCollector {
+
+    @Override
+    public boolean collectCompletions(Document doc, int offset, Completion.Context context, Consumer<Completion> consumer) {
+        AtomicBoolean ret = new AtomicBoolean(true);
+        if (JavadocCompletionUtils.isJavadocContext(doc, offset)) {
+            try {
+                ParserManager.parse(Collections.singletonList(Source.create(doc)), new UserTask() {
+                    @Override
+                    public void run(ResultIterator resultIterator) throws Exception {
+                        CompilationController controller = CompilationController.get(resultIterator.getParserResult(offset));
+                        controller.toPhase(JavaSource.Phase.ELEMENTS_RESOLVED);
+                        JavadocCompletionTask<Completion> task = JavadocCompletionTask.create(offset, new ItemFactoryImpl(controller, offset),
+                                context.getTriggerKind() == Completion.TriggerKind.TriggerForIncompleteCompletions, () -> false);
+                        task.run(resultIterator);
+                        List<Completion> results = task.getResults();
+                        if (results != null) {
+                            for (Iterator<Completion> it = results.iterator(); it.hasNext();) {
+                                Completion item = it.next();
+                                if (item == null) {
+                                    it.remove();
+                                }
+                            }
+                            results.forEach(consumer);
+                        }
+                        if (task.hasAdditionalItems()) {
+                            ret.set(false);
+                        }
+                    }
+                });
+            } catch (ParseException ex) {
+                Exceptions.printStackTrace(ex);

Review Comment:
   maybe rethrow as a `RuntimeException` ?



##########
java/java.editor/src/org/netbeans/modules/editor/java/JavaCompletionCollector.java:
##########
@@ -110,57 +110,178 @@ public class JavaCompletionCollector implements CompletionCollector {
     @Override
     public boolean collectCompletions(Document doc, int offset, Completion.Context context, Consumer<Completion> consumer) {
         AtomicBoolean ret = new AtomicBoolean(true);
-        try {
-            ParserManager.parse(Collections.singletonList(Source.create(doc)), new UserTask() {
-                @Override
-                public void run(ResultIterator resultIterator) throws Exception {
-                    TokenSequence<JavaTokenId> ts = null;
-                    for (TokenSequence<?> cand : resultIterator.getSnapshot().getTokenHierarchy().embeddedTokenSequences(offset, false)) {
-                        if (cand.language() == JavaTokenId.language()) {
-                            ts = (TokenSequence<JavaTokenId>) cand;
-                            break;
-                        }
-                    }
-                    if (ts == null) {//No Java on this offset
-                        return ;
-                    }
-                    if (ts.move(offset) == 0 || !ts.moveNext()) {
-                        if (!ts.movePrevious()) {
-                            ts.moveNext();
+        if (Utilities.isJavaContext(doc, offset, true)) {
+            try {
+                ParserManager.parse(Collections.singletonList(Source.create(doc)), new UserTask() {
+                    @Override
+                    public void run(ResultIterator resultIterator) throws Exception {
+                        TokenSequence<JavaTokenId> ts = SourceUtils.getJavaTokenSequence(resultIterator.getSnapshot().getTokenHierarchy(), offset);
+                        if (ts.move(offset) == 0 || !ts.moveNext()) {
+                            if (!ts.movePrevious()) {
+                                ts.moveNext();
+                            }
                         }
-                    }
-                    int len = offset - ts.offset();
-                    boolean combinedCompletion = context != null && context.getTriggerKind() == Completion.TriggerKind.TriggerForIncompleteCompletions
-                            || len > 0 && ts.token().length() >= len && ts.token().id() == JavaTokenId.IDENTIFIER;
-                    CompilationController controller = CompilationController.get(resultIterator.getParserResult(ts.offset()));
-                    controller.toPhase(JavaSource.Phase.RESOLVED);
-                    JavaCompletionTask<Completion> task = JavaCompletionTask.create(offset, new ItemFactoryImpl(controller, offset), combinedCompletion ? EnumSet.of(JavaCompletionTask.Options.COMBINED_COMPLETION) : EnumSet.noneOf(JavaCompletionTask.Options.class), () -> false);
-                    task.run(resultIterator);
-                    List<Completion> results = task.getResults();
-                    if (results != null) {
-                        for (Iterator<Completion> it = results.iterator(); it.hasNext();) {
-                            Completion item = it.next();
-                            if (item == null) {
-                                it.remove();
+                        int len = offset - ts.offset();
+                        boolean combinedCompletion = context != null && context.getTriggerKind() == Completion.TriggerKind.TriggerForIncompleteCompletions
+                                || len > 0 && ts.token().length() >= len && ts.token().id() == JavaTokenId.IDENTIFIER;
+                        CompilationController controller = CompilationController.get(resultIterator.getParserResult(ts.offset()));
+                        controller.toPhase(JavaSource.Phase.RESOLVED);
+                        JavaCompletionTask<Completion> task = JavaCompletionTask.create(offset, new ItemFactoryImpl(controller, offset), combinedCompletion ? EnumSet.of(JavaCompletionTask.Options.COMBINED_COMPLETION) : EnumSet.noneOf(JavaCompletionTask.Options.class), () -> false);
+                        task.run(resultIterator);
+                        List<Completion> results = task.getResults();
+                        if (results != null) {
+                            for (Iterator<Completion> it = results.iterator(); it.hasNext();) {
+                                Completion item = it.next();
+                                if (item == null) {
+                                    it.remove();
+                                }
                             }
+                            results.forEach(consumer);
+                        }
+                        if (task.hasAdditionalClasses() || task.hasAdditionalMembers()) {
+                            ret.set(false);
                         }
-                        results.forEach(consumer);
-                    }
-                    if (task.hasAdditionalClasses() || task.hasAdditionalMembers()) {
-                        ret.set(false);
                     }
-                }
-            });
-        } catch (ParseException ex) {
-            Exceptions.printStackTrace(ex);
+                });
+            } catch (ParseException ex) {
+                Exceptions.printStackTrace(ex);
+            }
         }
         return ret.get();
     }
 
+    public static final Set<String> SUPPORTED_ELEMENT_KINDS = new HashSet<>(Arrays.asList("PACKAGE", "CLASS", "INTERFACE", "ENUM", "ANNOTATION_TYPE", "METHOD", "CONSTRUCTOR", "INSTANCE_INIT", "STATIC_INIT", "FIELD", "ENUM_CONSTANT", "TYPE_PARAMETER", "MODULE"));
+
+    public static Supplier<String> getDocumentation(Document doc, int offset, ElementHandle handle) {
+        return () -> {
+            try {
+                JavaDocumentationTask<Future<String>> task = JavaDocumentationTask.create(offset, handle, new JavaDocumentationTask.DocumentationFactory<Future<String>>() {
+                    @Override
+                    public Future<String> create(CompilationInfo compilationInfo, Element element, Callable<Boolean> cancel) {
+                        return ElementJavadoc.create(compilationInfo, element, cancel).getTextAsync();
+                    }
+                }, () -> false);
+                ParserManager.parse(Collections.singletonList(Source.create(doc)), new UserTask() {
+                    @Override
+                    public void run(ResultIterator resultIterator) throws Exception {
+                        task.run(resultIterator);
+                    }
+                });
+                return task.getDocumentation().get();
+            } catch (Exception ex) {
+                throw new RuntimeException(ex);
+            }
+        };
+    }
+
+    public static Completion.Kind elementKind2CompletionItemKind(ElementKind kind) {

Review Comment:
   This is in `ElementHeaders.javaKind2Structure` as a helper method, don't know if it is feasible to use here.



##########
java/java.editor/src/org/netbeans/modules/editor/java/JavaCompletionCollector.java:
##########
@@ -110,57 +110,178 @@ public class JavaCompletionCollector implements CompletionCollector {
     @Override
     public boolean collectCompletions(Document doc, int offset, Completion.Context context, Consumer<Completion> consumer) {
         AtomicBoolean ret = new AtomicBoolean(true);
-        try {
-            ParserManager.parse(Collections.singletonList(Source.create(doc)), new UserTask() {
-                @Override
-                public void run(ResultIterator resultIterator) throws Exception {
-                    TokenSequence<JavaTokenId> ts = null;
-                    for (TokenSequence<?> cand : resultIterator.getSnapshot().getTokenHierarchy().embeddedTokenSequences(offset, false)) {
-                        if (cand.language() == JavaTokenId.language()) {
-                            ts = (TokenSequence<JavaTokenId>) cand;
-                            break;
-                        }
-                    }
-                    if (ts == null) {//No Java on this offset
-                        return ;
-                    }
-                    if (ts.move(offset) == 0 || !ts.moveNext()) {
-                        if (!ts.movePrevious()) {
-                            ts.moveNext();
+        if (Utilities.isJavaContext(doc, offset, true)) {
+            try {
+                ParserManager.parse(Collections.singletonList(Source.create(doc)), new UserTask() {
+                    @Override
+                    public void run(ResultIterator resultIterator) throws Exception {
+                        TokenSequence<JavaTokenId> ts = SourceUtils.getJavaTokenSequence(resultIterator.getSnapshot().getTokenHierarchy(), offset);
+                        if (ts.move(offset) == 0 || !ts.moveNext()) {
+                            if (!ts.movePrevious()) {
+                                ts.moveNext();
+                            }
                         }
-                    }
-                    int len = offset - ts.offset();
-                    boolean combinedCompletion = context != null && context.getTriggerKind() == Completion.TriggerKind.TriggerForIncompleteCompletions
-                            || len > 0 && ts.token().length() >= len && ts.token().id() == JavaTokenId.IDENTIFIER;
-                    CompilationController controller = CompilationController.get(resultIterator.getParserResult(ts.offset()));
-                    controller.toPhase(JavaSource.Phase.RESOLVED);
-                    JavaCompletionTask<Completion> task = JavaCompletionTask.create(offset, new ItemFactoryImpl(controller, offset), combinedCompletion ? EnumSet.of(JavaCompletionTask.Options.COMBINED_COMPLETION) : EnumSet.noneOf(JavaCompletionTask.Options.class), () -> false);
-                    task.run(resultIterator);
-                    List<Completion> results = task.getResults();
-                    if (results != null) {
-                        for (Iterator<Completion> it = results.iterator(); it.hasNext();) {
-                            Completion item = it.next();
-                            if (item == null) {
-                                it.remove();
+                        int len = offset - ts.offset();
+                        boolean combinedCompletion = context != null && context.getTriggerKind() == Completion.TriggerKind.TriggerForIncompleteCompletions
+                                || len > 0 && ts.token().length() >= len && ts.token().id() == JavaTokenId.IDENTIFIER;
+                        CompilationController controller = CompilationController.get(resultIterator.getParserResult(ts.offset()));
+                        controller.toPhase(JavaSource.Phase.RESOLVED);
+                        JavaCompletionTask<Completion> task = JavaCompletionTask.create(offset, new ItemFactoryImpl(controller, offset), combinedCompletion ? EnumSet.of(JavaCompletionTask.Options.COMBINED_COMPLETION) : EnumSet.noneOf(JavaCompletionTask.Options.class), () -> false);
+                        task.run(resultIterator);
+                        List<Completion> results = task.getResults();
+                        if (results != null) {
+                            for (Iterator<Completion> it = results.iterator(); it.hasNext();) {
+                                Completion item = it.next();
+                                if (item == null) {
+                                    it.remove();
+                                }
                             }
+                            results.forEach(consumer);
+                        }
+                        if (task.hasAdditionalClasses() || task.hasAdditionalMembers()) {
+                            ret.set(false);
                         }
-                        results.forEach(consumer);
-                    }
-                    if (task.hasAdditionalClasses() || task.hasAdditionalMembers()) {
-                        ret.set(false);
                     }
-                }
-            });
-        } catch (ParseException ex) {
-            Exceptions.printStackTrace(ex);
+                });
+            } catch (ParseException ex) {
+                Exceptions.printStackTrace(ex);
+            }
         }
         return ret.get();
     }
 
+    public static final Set<String> SUPPORTED_ELEMENT_KINDS = new HashSet<>(Arrays.asList("PACKAGE", "CLASS", "INTERFACE", "ENUM", "ANNOTATION_TYPE", "METHOD", "CONSTRUCTOR", "INSTANCE_INIT", "STATIC_INIT", "FIELD", "ENUM_CONSTANT", "TYPE_PARAMETER", "MODULE"));
+
+    public static Supplier<String> getDocumentation(Document doc, int offset, ElementHandle handle) {
+        return () -> {
+            try {
+                JavaDocumentationTask<Future<String>> task = JavaDocumentationTask.create(offset, handle, new JavaDocumentationTask.DocumentationFactory<Future<String>>() {
+                    @Override
+                    public Future<String> create(CompilationInfo compilationInfo, Element element, Callable<Boolean> cancel) {
+                        return ElementJavadoc.create(compilationInfo, element, cancel).getTextAsync();
+                    }
+                }, () -> false);
+                ParserManager.parse(Collections.singletonList(Source.create(doc)), new UserTask() {
+                    @Override
+                    public void run(ResultIterator resultIterator) throws Exception {
+                        task.run(resultIterator);
+                    }
+                });
+                return task.getDocumentation().get();
+            } catch (Exception ex) {
+                throw new RuntimeException(ex);
+            }
+        };
+    }
+
+    public static Completion.Kind elementKind2CompletionItemKind(ElementKind kind) {
+        switch (kind) {
+            case PACKAGE:
+                return Completion.Kind.Folder;
+            case ENUM:
+                return Completion.Kind.Enum;
+            case CLASS:
+                return Completion.Kind.Class;
+            case ANNOTATION_TYPE:
+                return Completion.Kind.Interface;
+            case INTERFACE:
+                return Completion.Kind.Interface;
+            case ENUM_CONSTANT:
+                return Completion.Kind.EnumMember;
+            case FIELD:
+                return Completion.Kind.Field;
+            case PARAMETER:
+                return Completion.Kind.Variable;
+            case LOCAL_VARIABLE:
+                return Completion.Kind.Variable;
+            case EXCEPTION_PARAMETER:
+                return Completion.Kind.Variable;
+            case METHOD:
+                return Completion.Kind.Method;
+            case CONSTRUCTOR:
+                return Completion.Kind.Constructor;
+            case TYPE_PARAMETER:
+                return Completion.Kind.TypeParameter;
+            case RESOURCE_VARIABLE:
+                return Completion.Kind.Variable;
+            case MODULE:
+                return Completion.Kind.Module;
+            case STATIC_INIT:
+            case INSTANCE_INIT:
+            case OTHER:
+            default:
+                return Completion.Kind.Text;
+        }
+    }
+
+    public static Supplier<List<TextEdit>> addImport(Document doc, ElementHandle<?> handle) {
+        return () -> {
+            try {
+                TextEdit textEdit = modify2TextEdit(JavaSource.forDocument(doc), copy -> {
+                    copy.toPhase(JavaSource.Phase.RESOLVED);
+                    Element e = handle.resolve(copy);
+                    if (e != null) {
+                        copy.rewrite(copy.getCompilationUnit(), GeneratorUtilities.get(copy).addImports(copy.getCompilationUnit(), Collections.singleton(e)));
+                    }
+                });
+                return textEdit != null ? Collections.singletonList(textEdit) : null;
+            } catch (IOException ex) {
+                throw new RuntimeException(ex);
+            }
+        };
+    }
+
+    public static boolean isOfKind(Element e, EnumSet<ElementKind> kinds) {
+        if (kinds.contains(e.getKind())) {
+            return true;
+        }
+        for (Element ee : e.getEnclosedElements()) {
+            if (isOfKind(ee, kinds)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public static boolean isInDefaultPackage(Element e) {
+        while (e != null && e.getKind() != ElementKind.PACKAGE) {
+            e = e.getEnclosingElement();
+        }
+        return e != null && e.getSimpleName().length() == 0;
+    }
+
+    private static TextEdit modify2TextEdit(JavaSource js, Task<WorkingCopy> task) throws IOException {

Review Comment:
   Could be nice utility too, right now we have the same code in `java.lsp.server`, `java.editor` and `java.hints` ... but where should it be placed ?



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@netbeans.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@netbeans.apache.org
For additional commands, e-mail: notifications-help@netbeans.apache.org

For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists