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 ebce17f LSP: Provide hover information in Micronaut yaml files. (#2955) ebce17f is described below commit ebce17f24363206e0e50c16b0d63962a282a670a Author: Dusan Balek <dusan.ba...@oracle.com> AuthorDate: Thu May 20 18:24:37 2021 +0200 LSP: Provide hover information in Micronaut yaml files. (#2955) --- enterprise/micronaut/nbproject/project.xml | 2 +- .../MicronautConfigCompletionProvider.java | 2 +- .../completion/MicronautHoverProvider.java | 40 + ide/api.lsp/apichanges.xml | 15 + ide/api.lsp/arch.xml | 1097 ++++++++++++++++++++ ide/api.lsp/manifest.mf | 2 +- ide/api.lsp/nbproject/project.properties | 3 + ide/api.lsp/nbproject/project.xml | 10 + ide/api.lsp/src/org/netbeans/api/lsp/Hover.java | 67 ++ .../org/netbeans/api/lsp/HyperlinkLocation.java | 6 +- .../src/org/netbeans/spi/lsp/HoverProvider.java | 56 + .../unit/src/org/netbeans/api/lsp/HoverTest.java | 80 ++ .../netbeans/api/lsp/HyperlinkLocationTest.java | 136 +++ java/java.editor/nbproject/project.xml | 2 +- .../modules/editor/java/JavaHoverProvider.java | 65 ++ .../server/protocol/TextDocumentServiceImpl.java | 64 +- .../java/lsp/server/protocol/ServerTest.java | 1 + .../api/java/source/ui/ElementJavadoc.java | 72 +- nbbuild/build.properties | 1 + nbbuild/javadoctools/links.xml | 1 + nbbuild/javadoctools/properties.xml | 1 + nbbuild/javadoctools/replaces.xml | 1 + 22 files changed, 1617 insertions(+), 107 deletions(-) diff --git a/enterprise/micronaut/nbproject/project.xml b/enterprise/micronaut/nbproject/project.xml index e15395e..a64839a 100644 --- a/enterprise/micronaut/nbproject/project.xml +++ b/enterprise/micronaut/nbproject/project.xml @@ -48,7 +48,7 @@ <compile-dependency/> <run-dependency> <release-version>1</release-version> - <specification-version>1.0</specification-version> + <specification-version>1.2</specification-version> </run-dependency> </dependency> <dependency> diff --git a/enterprise/micronaut/src/org/netbeans/modules/micronaut/completion/MicronautConfigCompletionProvider.java b/enterprise/micronaut/src/org/netbeans/modules/micronaut/completion/MicronautConfigCompletionProvider.java index 788e0da..29aca81 100644 --- a/enterprise/micronaut/src/org/netbeans/modules/micronaut/completion/MicronautConfigCompletionProvider.java +++ b/enterprise/micronaut/src/org/netbeans/modules/micronaut/completion/MicronautConfigCompletionProvider.java @@ -110,7 +110,7 @@ public class MicronautConfigCompletionProvider implements CompletionProvider { if (element == null) { element = MicronautConfigUtilities.resolveProperty(doc, caretOffset, null, null); } - resultSet.setDocumentation(new MicronautConfigDocumentation(element)); + resultSet.setDocumentation(element != null ? new MicronautConfigDocumentation(element) : null); resultSet.finish(); } } diff --git a/enterprise/micronaut/src/org/netbeans/modules/micronaut/completion/MicronautHoverProvider.java b/enterprise/micronaut/src/org/netbeans/modules/micronaut/completion/MicronautHoverProvider.java new file mode 100644 index 0000000..be9fa4c --- /dev/null +++ b/enterprise/micronaut/src/org/netbeans/modules/micronaut/completion/MicronautHoverProvider.java @@ -0,0 +1,40 @@ +/* + * 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.micronaut.completion; + +import java.util.concurrent.CompletableFuture; +import javax.swing.text.Document; +import org.netbeans.api.editor.mimelookup.MimeRegistration; +import org.netbeans.modules.micronaut.MicronautConfigUtilities; +import org.netbeans.spi.lsp.HoverProvider; +import org.springframework.boot.configurationmetadata.ConfigurationMetadataProperty; + +/** + * + * @author Dusan Balek + */ +@MimeRegistration(mimeType = "text/x-yaml", service = HoverProvider.class) +public class MicronautHoverProvider implements HoverProvider { + + @Override + public CompletableFuture<String> getHoverContent(Document doc, int offset) { + ConfigurationMetadataProperty property = MicronautConfigUtilities.resolveProperty(doc, offset, null, null); + return CompletableFuture.completedFuture(property != null ? new MicronautConfigDocumentation(property).getText() : null); + } +} diff --git a/ide/api.lsp/apichanges.xml b/ide/api.lsp/apichanges.xml index e533835..680a177 100644 --- a/ide/api.lsp/apichanges.xml +++ b/ide/api.lsp/apichanges.xml @@ -47,6 +47,21 @@ <!-- ACTUAL CHANGES BEGIN HERE: --> <changes> + <change id="HoverProvider"> + <api name="LSP_API"/> + <summary>Adding Hover and HoverProvider</summary> + <version major="1" minor="2"/> + <date day="13" month="5" year="2021"/> + <author login="dbalek"/> + <compatibility binary="compatible" source="compatible" addition="yes" deletion="no"/> + <description> + A <code>Hover</code> class and <code>HoverProvider</code> interface introduced + that allows to resolve a hover information at a given document offset and + return its content. + </description> + <class package="org.netbeans.api.lsp" name="Hover"/> + <class package="org.netbeans.spi.lsp" name="HoverProvider"/> + </change> <change id="HyperlinkTypeDefLocationProvider"> <api name="LSP_API"/> <summary>Adding HyperlinkTypeDefLocationProvider</summary> diff --git a/ide/api.lsp/arch.xml b/ide/api.lsp/arch.xml new file mode 100644 index 0000000..1f086e8 --- /dev/null +++ b/ide/api.lsp/arch.xml @@ -0,0 +1,1097 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + + 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. + +--> +<!DOCTYPE api-answers PUBLIC "-//NetBeans//DTD Arch Answers//EN" "../../nbbuild/antsrc/org/netbeans/nbbuild/Arch.dtd" [ + <!ENTITY api-questions SYSTEM "../../nbbuild/antsrc/org/netbeans/nbbuild/Arch-api-questions.xml"> +]> + +<api-answers + question-version="1.28" + module="LSP APIs" + author="dba...@netbeans.org" +> + + &api-questions; + + +<!-- + <question id="arch-overall" when="init"> + Describe the overall architecture. + <hint> + What will be API for + <a href="http://openide.netbeans.org/tutorial/api-design.html#design.apiandspi"> + clients and what support API</a>? + What parts will be pluggable? + How will plug-ins be registered? Please use <code><api type="export"/></code> + to describe your general APIs and specify their + <a href="http://openide.netbeans.org/tutorial/api-design.html#category-private"> + stability categories</a>. + If possible please provide simple diagrams. + </hint> + </question> +--> + <answer id="arch-overall"> + <api group="java" name="LanguageServerAPIs" type="export" category="official"> + The Lanugage Server APIs provides basic facilities for getting or supplying + information used by language server implementations. + </api> + </answer> + + + +<!-- + <question id="arch-quality" when="init"> + How will the <a href="http://www.netbeans.org/community/guidelines/q-evangelism.html">quality</a> + of your code be tested and + how are future regressions going to be prevented? + <hint> + What kind of testing do + you want to use? How much functionality, in which areas, + should be covered by the tests? How you find out that your + project was successful? + </hint> + </question> +--> + <answer id="arch-quality"> + <p> + XXX no answer for arch-quality + </p> + </answer> + + + +<!-- + <question id="arch-time" when="init"> + What are the time estimates of the work? + <hint> + Please express your estimates of how long the design, implementation, + stabilization are likely to last. How many people will be needed to + implement this and what is the expected milestone by which the work should be + ready? + </hint> + </question> +--> + <answer id="arch-time"> + <p> + XXX no answer for arch-time + </p> + </answer> + + + +<!-- + <question id="arch-usecases" when="init"> + <hint> + Content of this answer will be displayed as part of page at + http://www.netbeans.org/download/dev/javadoc/usecases.html + You can use tags <usecase name="name> regular html description </usecase> + and if you want to use an URL you can prefix if with @TOP@ to begin + at the root of your javadoc + </hint> + + Describe the main <a href="http://openide.netbeans.org/tutorial/api-design.html#usecase"> + use cases</a> of the new API. Who will use it under + what circumstances? What kind of code would typically need to be written + to use the module? + </question> +--> + <answer id="arch-usecases"> + <p> + XXX no answer for arch-usecases + </p> + </answer> + + + +<!-- + <question id="arch-what" when="init"> + What is this project good for? + <hint> + Please provide here a few lines describing the project, + what problem it should solve, provide links to documentation, + specifications, etc. + </hint> + </question> +--> + <answer id="arch-what"> + <p> + XXX no answer for arch-what + </p> + </answer> + + + +<!-- + <question id="arch-where" when="impl"> + Where one can find sources for your module? + <hint> + Please provide link to the CVS web client at + http://www.netbeans.org/download/source_browse.html + or just use tag defaultanswer generate='here' + </hint> + </question> +--> + <answer id="arch-where"> + <defaultanswer generate='here' /> + </answer> + + + +<!-- + <question id="compat-deprecation" when="init"> + How the introduction of your project influences functionality + provided by previous version of the product? + <hint> + If you are planning to deprecate/remove/change any existing APIs, + list them here accompanied with the reason explaining why you + are doing so. + </hint> + </question> +--> + <answer id="compat-deprecation"> + <p> + XXX no answer for compat-deprecation + </p> + </answer> + + + +<!-- + <question id="compat-i18n" when="impl"> + Is your module correctly internationalized? + <hint> + Correct internationalization means that it obeys instructions + at <a href="http://www.netbeans.org/download/dev/javadoc/org-openide-modules/org/openide/modules/doc-files/i18n-branding.html"> + NetBeans I18N pages</a>. + </hint> + </question> +--> + <answer id="compat-i18n"> + <p> + XXX no answer for compat-i18n + </p> + </answer> + + + +<!-- + <question id="compat-standards" when="init"> + Does the module implement or define any standards? Is the + implementation exact or does it deviate somehow? + </question> +--> + <answer id="compat-standards"> + <p> + XXX no answer for compat-standards + </p> + </answer> + + + +<!-- + <question id="compat-version" when="impl"> + Can your module coexist with earlier and future + versions of itself? Can you correctly read all old settings? Will future + versions be able to read your current settings? Can you read + or politely ignore settings stored by a future version? + + <hint> + Very helpful for reading settings is to store version number + there, so future versions can decide whether how to read/convert + the settings and older versions can ignore the new ones. + </hint> + </question> +--> + <answer id="compat-version"> + <p> + XXX no answer for compat-version + </p> + </answer> + + + +<!-- + <question id="dep-jre" when="final"> + Which version of JRE do you need (1.2, 1.3, 1.4, etc.)? + <hint> + It is expected that if your module runs on 1.x that it will run + on 1.x+1 if no, state that please. Also describe here cases where + you run different code on different versions of JRE and why. + </hint> + </question> +--> + <answer id="dep-jre"> + <p> + XXX no answer for dep-jre + </p> + </answer> + + + +<!-- + <question id="dep-jrejdk" when="final"> + Do you require the JDK or is the JRE enough? + </question> +--> + <answer id="dep-jrejdk"> + <p> + XXX no answer for dep-jrejdk + </p> + </answer> + + + +<!-- + <question id="dep-nb" when="init"> + What other NetBeans projects and modules does this one depend on? + <hint> + Depending on other NetBeans projects influnces the ability of + users of your work to customize their own branded version of + NetBeans by enabling and disabling some modules. Too + much dependencies restrict this kind of customization. If that + is your case, then you may want to split your functionality into + pieces of autoload, eager and regular modules which can be + enabled independently. Usually the answer to this question + is generated from your <code>project.xml</code> file, but + if it is not guessed correctly, you can suppress it by + specifying <defaultanswer generate="none"/> and + write here your own. Please describe such projects as imported APIs using + the <code><api name="identification" type="import or export" category="stable" url="where is the description" /></code>. + By doing this information gets listed in the summary page of your + javadoc. + </hint> + </question> +--> + <answer id="dep-nb"> + <defaultanswer generate='here' /> + </answer> + + + +<!-- + <question id="dep-non-nb" when="init"> + What other projects outside NetBeans does this one depend on? + + <hint> + Depending on 3rd party libraries is always problematic, + especially if they are not open source, as that complicates + the licensing scheme of NetBeans. Please enumerate your + external dependencies here, so it is correctly understood since + the begining what are the legal implications of your project. + Also please note that + some non-NetBeans projects are packaged as NetBeans modules + (see <a href="http://libs.netbeans.org/">libraries</a>) and + it is preferred to use this approach when more modules may + depend and share such third-party libraries. + </hint> + </question> +--> + <answer id="dep-non-nb"> + <p> + XXX no answer for dep-non-nb + </p> + </answer> + + + +<!-- + <question id="dep-platform" when="init"> + On which platforms does your module run? Does it run in the same + way on each? + <hint> + If you plan any dependency on OS or any usage of native code, + please describe why you are doing so and describe how you envision + to enforce the portability of your code. + Please note that there is a support for <a href="http://www.netbeans.org/download/dev/javadoc/org-openide-modules/org/openide/modules/doc-files/api.html#how-os-specific">OS conditionally + enabled modules</a> which together with autoload/eager modules + can allow you to enable to provide the best OS aware support + on certain OSes while providing compatibility bridge on the not + supported ones. + Also please list the supported + OSes/HW platforms and mentioned the lovest version of JDK required + for your project to run on. Also state whether JRE is enough or + you really need JDK. + </hint> + </question> +--> + <answer id="dep-platform"> + <p> + XXX no answer for dep-platform + </p> + </answer> + + + +<!-- + <question id="deploy-dependencies" when="final"> + What do other modules need to do to declare a dependency on this one, + in addition to or instead of the normal module dependency declaration + (e.g. tokens to require)? + <hint> + Provide a sample of the actual lines you would add to a module manifest + to declare a dependency, for example OpenIDE-Module-Requires: some.token. + If other modules should not depend on this module, or should just use a + simple regular module dependency, you can just answer "nothing". If you + intentionally expose a semistable API to clients using implementation + dependencies, you should mention that here (but there is no need to give + an example of usage). + </hint> + </question> +--> + <answer id="deploy-dependencies"> + <p> + XXX no answer for deploy-dependencies + </p> + </answer> + + + +<!-- + <question id="deploy-jar" when="impl"> + Do you deploy just module JAR file(s) or other files as well? + <hint> + Usually a module consist of one JAR file (perhaps with Class-Path + extensions) and also a configuration file that enables it. If you + have any other files, use + <api group="java.io.File" name="yourname" type="export" category="friend">...</api> + to define the location, name and stability of your files (of course + changing "yourname" and "friend" to suit your needs). + + If it uses more than one JAR, describe where they are located, how + they refer to each other. + If it consist of module JAR(s) and other files, please describe + what is their purpose, why other files are necessary. Please + make sure that installation/uninstallation leaves the system + in state as it was before installation. + </hint> + </question> +--> + <answer id="deploy-jar"> + <p> + XXX no answer for deploy-jar + </p> + </answer> + + + +<!-- + <question id="deploy-nbm" when="impl"> + Can you deploy an NBM via the Update Center? + <hint> + If not why? + </hint> + </question> +--> + <answer id="deploy-nbm"> + <p> + XXX no answer for deploy-nbm + </p> + </answer> + + + +<!-- + <question id="deploy-packages" when="init"> + Are packages of your module made inaccessible by not declaring them + public? + + <hint> + By default NetBeans build harness treats all packages are private. + If you export some of them - either as public or friend packages, + you should have a reason. If the reason is described elsewhere + in this document, you can ignore this question. + </hint> + </question> +--> + <answer id="deploy-packages"> + <p> + XXX no answer for deploy-packages + </p> + </answer> + + + +<!-- + <question id="deploy-shared" when="final"> + Do you need to be installed in the shared location only, or in the user directory only, + or can your module be installed anywhere? + <hint> + Installation location shall not matter, if it does explain why. + Consider also whether <code>InstalledFileLocator</code> can help. + </hint> + </question> +--> + <answer id="deploy-shared"> + <p> + XXX no answer for deploy-shared + </p> + </answer> + + + +<!-- + <question id="exec-ant-tasks" when="impl"> + Do you define or register any ant tasks that other can use? + + <hint> + If you provide an ant task that users can use, you need to be very + careful about its syntax and behaviour, as it most likely forms an + API for end users and as there is a lot of end users, their reaction + when such API gets broken can be pretty strong. + </hint> + </question> +--> + <answer id="exec-ant-tasks"> + <p> + XXX no answer for exec-ant-tasks + </p> + </answer> + + + +<!-- + <question id="exec-classloader" when="impl"> + Does your code create its own class loader(s)? + <hint> + A bit unusual. Please explain why and what for. + </hint> + </question> +--> + <answer id="exec-classloader"> + <p> + XXX no answer for exec-classloader + </p> + </answer> + + + +<!-- + <question id="exec-component" when="impl"> + Is execution of your code influenced by any (string) property + of any of your components? + + <hint> + Often <code>JComponent.getClientProperty</code>, <code>Action.getValue</code> + or <code>PropertyDescriptor.getValue</code>, etc. are used to influence + a behavior of some code. This of course forms an interface that should + be documented. Also if one depends on some interface that an object + implements (<code>component instanceof Runnable</code>) that forms an + API as well. + </hint> + </question> +--> + <answer id="exec-component"> + <p> + XXX no answer for exec-component + </p> + </answer> + + + +<!-- + <question id="exec-introspection" when="impl"> + Does your module use any kind of runtime type information (<code>instanceof</code>, + work with <code>java.lang.Class</code>, etc.)? + <hint> + Check for cases when you have an object of type A and you also + expect it to (possibly) be of type B and do some special action. That + should be documented. The same applies on operations in meta-level + (Class.isInstance(...), Class.isAssignableFrom(...), etc.). + </hint> + </question> +--> + <answer id="exec-introspection"> + <p> + XXX no answer for exec-introspection + </p> + </answer> + + + +<!-- + <question id="exec-privateaccess" when="final"> + Are you aware of any other parts of the system calling some of + your methods by reflection? + <hint> + If so, describe the "contract" as an API. Likely private or friend one, but + still API and consider rewrite of it. + </hint> + </question> +--> + <answer id="exec-privateaccess"> + <p> + XXX no answer for exec-privateaccess + </p> + </answer> + + + +<!-- + <question id="exec-process" when="impl"> + Do you execute an external process from your module? How do you ensure + that the result is the same on different platforms? Do you parse output? + Do you depend on result code? + <hint> + If you feed an input, parse the output please declare that as an API. + </hint> + </question> +--> + <answer id="exec-process"> + <p> + XXX no answer for exec-process + </p> + </answer> + + + +<!-- + <question id="exec-property" when="impl"> + Is execution of your code influenced by any environment or + Java system (<code>System.getProperty</code>) property? + + <hint> + If there is a property that can change the behavior of your + code, somebody will likely use it. You should describe what it does + and the <a href="http://openide.netbeans.org/tutorial/api-design.html#life">stability category</a> + of this API. You may use + <pre> + <api type="export" group="property" name="id" category="private" url="http://..."> + description of the property, where it is used, what it influence, etc. + </api> + </pre> + </hint> + </question> +--> + <answer id="exec-property"> + <p> + XXX no answer for exec-property + </p> + </answer> + + + +<!-- + <question id="exec-reflection" when="impl"> + Does your code use Java Reflection to execute other code? + <hint> + This usually indicates a missing or insufficient API in the other + part of the system. If the other side is not aware of your dependency + this contract can be easily broken. + </hint> + </question> +--> + <answer id="exec-reflection"> + <p> + XXX no answer for exec-reflection + </p> + </answer> + + + +<!-- + <question id="exec-threading" when="init"> + What threading models, if any, does your module adhere to? How the + project behaves with respect to threading? + <hint> + Is your API threadsafe? Can it be accessed from any threads or + just from some dedicated ones? Any special relation to AWT and + its Event Dispatch thread? Also + if your module calls foreign APIs which have a specific threading model, + indicate how you comply with the requirements for multithreaded access + (synchronization, mutexes, etc.) applicable to those APIs. + If your module defines any APIs, or has complex internal structures + that might be used from multiple threads, declare how you protect + data against concurrent access, race conditions, deadlocks, etc., + and whether such rules are enforced by runtime warnings, errors, assertions, etc. + Examples: a class might be non-thread-safe (like Java Collections); might + be fully thread-safe (internal locking); might require access through a mutex + (and may or may not automatically acquire that mutex on behalf of a client method); + might be able to run only in the event queue; etc. + Also describe when any events are fired: synchronously, asynchronously, etc. + Ideas: <a href="http://core.netbeans.org/proposals/threading/index.html#recommendations">Threading Recommendations</a> (in progress) + </hint> + </question> +--> + <answer id="exec-threading"> + <p> + XXX no answer for exec-threading + </p> + </answer> + + + +<!-- + <question id="format-clipboard" when="impl"> + Which data flavors (if any) does your code read from or insert to + the clipboard (by access to clipboard on means calling methods on <code>java.awt.datatransfer.Transferable</code>? + + <hint> + Often Node's deal with clipboard by usage of <code>Node.clipboardCopy, Node.clipboardCut and Node.pasteTypes</code>. + Check your code for overriding these methods. + </hint> + </question> +--> + <answer id="format-clipboard"> + <p> + XXX no answer for format-clipboard + </p> + </answer> + + + +<!-- + <question id="format-dnd" when="impl"> + Which protocols (if any) does your code understand during Drag & Drop? + <hint> + Often Node's deal with clipboard by usage of <code>Node.drag, Node.getDropType</code>. + Check your code for overriding these methods. Btw. if they are not overridden, they + by default delegate to <code>Node.clipboardCopy, Node.clipboardCut and Node.pasteTypes</code>. + </hint> + </question> +--> + <answer id="format-dnd"> + <p> + XXX no answer for format-dnd + </p> + </answer> + + + +<!-- + <question id="format-types" when="impl"> + Which protocols and file formats (if any) does your module read or write on disk, + or transmit or receive over the network? Do you generate an ant build script? + Can it be edited and modified? + + <hint> + <p> + Files can be read and written by other programs, modules and users. If they influence + your behaviour, make sure you either document the format or claim that it is a private + api (using the <api> tag). + </p> + + <p> + If you generate an ant build file, this is very likely going to be seen by end users and + they will be attempted to edit it. You should be ready for that and provide here a link + to documentation that you have for such purposes and also describe how you are going to + understand such files during next release, when you (very likely) slightly change the + format. + </p> + </hint> + </question> +--> + <answer id="format-types"> + <p> + XXX no answer for format-types + </p> + </answer> + + + +<!-- + <question id="lookup-lookup" when="init"> + Does your module use <code>org.openide.util.Lookup</code> + or any similar technology to find any components to communicate with? Which ones? + + <hint> + NetBeans is build around a generic registry of services called + lookup. It is preferable to use it for registration and discovery + if possible. See + <a href="http://www.netbeans.org/download/dev/javadoc/org-openide-util/org/openide/util/lookup/doc-files/index.html"> + The Solution to Comunication Between Components + </a>. If you do not plan to use lookup and insist usage + of other solution, then please describe why it is not working for + you. + <br/> + When filling the final version of your arch document, please + describe the interfaces you are searching for, where + are defined, whether you are searching for just one or more of them, + if the order is important, etc. Also classify the stability of such + API contract. Use <api group=&lookup& /> tag, so + your information gets listed in the summary page of your javadoc. + </hint> + </question> +--> + <answer id="lookup-lookup"> + <p> + XXX no answer for lookup-lookup + </p> + </answer> + + + +<!-- + <question id="lookup-register" when="final"> + Do you register anything into lookup for other code to find? + <hint> + Do you register using layer file or using <code>META-INF/services</code>? + Who is supposed to find your component? + </hint> + </question> +--> + <answer id="lookup-register"> + <p> + XXX no answer for lookup-register + </p> + </answer> + + + +<!-- + <question id="lookup-remove" when="final"> + Do you remove entries of other modules from lookup? + <hint> + Why? Of course, that is possible, but it can be dangerous. Is the module + your are masking resource from aware of what you are doing? + </hint> + </question> +--> + <answer id="lookup-remove"> + <p> + XXX no answer for lookup-remove + </p> + </answer> + + + +<!-- + <question id="perf-exit" when="final"> + Does your module run any code on exit? + </question> +--> + <answer id="perf-exit"> + <p> + XXX no answer for perf-exit + </p> + </answer> + + + +<!-- + <question id="perf-huge_dialogs" when="final"> + Does your module contain any dialogs or wizards with a large number of + GUI controls such as combo boxes, lists, trees, or text areas? + </question> +--> + <answer id="perf-huge_dialogs"> + <p> + XXX no answer for perf-huge_dialogs + </p> + </answer> + + + +<!-- + <question id="perf-limit" when="init"> + Are there any hard-coded or practical limits in the number or size of + elements your code can handle? + <hint> + Most of algorithms have increasing memory and speed complexity + with respect to size of data they operate on. What is the critical + part of your project that can be seen as a bottleneck with + respect to speed or required memory? What are the practical + sizes of data you tested your project with? What is your estimate + of potential size of data that would cause visible performance + problems? Is there some kind of check to detect such situation + and prevent "hard" crashes - for example the CloneableEditorSupport + checks for size of a file to be opened in editor + and if it is larger than 1Mb it shows a dialog giving the + user the right to decide - e.g. to cancel or commit suicide. + </hint> + </question> +--> + <answer id="perf-limit"> + <p> + XXX no answer for perf-limit + </p> + </answer> + + + +<!-- + <question id="perf-mem" when="final"> + How much memory does your component consume? Estimate + with a relation to the number of windows, etc. + </question> +--> + <answer id="perf-mem"> + <p> + XXX no answer for perf-mem + </p> + </answer> + + + +<!-- + <question id="perf-menus" when="final"> + Does your module use dynamically updated context menus, or + context-sensitive actions with complicated and slow enablement logic? + <hint> + If you do a lot of tricks when adding actions to regular or context menus, you can significantly + slow down display of the menu, even when the user is not using your action. Pay attention to + actions you add to the main menu bar, and to context menus of foreign nodes or components. If + the action is conditionally enabled, or changes its display dynamically, you need to check the + impact on performance. In some cases it may be more appropriate to make a simple action that is + always enabled but does more detailed checks in a dialog if it is actually run. + </hint> + </question> +--> + <answer id="perf-menus"> + <p> + XXX no answer for perf-menus + </p> + </answer> + + + +<!-- + <question id="perf-progress" when="final"> + Does your module execute any long-running tasks? + + <hint>Long running tasks should never block + AWT thread as it badly hurts the UI + <a href="http://performance.netbeans.org/responsiveness/issues.html"> + responsiveness</a>. + Tasks like connecting over + network, computing huge amount of data, compilation + be done asynchronously (for example + using <code>RequestProcessor</code>), definitively it should + not block AWT thread. + </hint> + </question> +--> + <answer id="perf-progress"> + <p> + XXX no answer for perf-progress + </p> + </answer> + + + +<!-- + <question id="perf-scale" when="init"> + Which external criteria influence the performance of your + program (size of file in editor, number of files in menu, + in source directory, etc.) and how well your code scales? + <hint> + Please include some estimates, there are other more detailed + questions to answer in later phases of implementation. + </hint> + </question> +--> + <answer id="perf-scale"> + <p> + XXX no answer for perf-scale + </p> + </answer> + + + +<!-- + <question id="perf-spi" when="init"> + How the performance of the plugged in code will be enforced? + <hint> + If you allow foreign code to be plugged into your own module, how + do you enforce that it will behave correctly and quickly and will not + negatively influence the performance of your own module? + </hint> + </question> +--> + <answer id="perf-spi"> + <p> + XXX no answer for perf-spi + </p> + </answer> + + + +<!-- + <question id="perf-startup" when="final"> + Does your module run any code on startup? + </question> +--> + <answer id="perf-startup"> + <p> + XXX no answer for perf-startup + </p> + </answer> + + + +<!-- + <question id="perf-wakeup" when="final"> + Does any piece of your code wake up periodically and do something + even when the system is otherwise idle (no user interaction)? + </question> +--> + <answer id="perf-wakeup"> + <p> + XXX no answer for perf-wakeup + </p> + </answer> + + + +<!-- + <question id="resources-file" when="final"> + Does your module use <code>java.io.File</code> directly? + + <hint> + NetBeans provide a logical wrapper over plain files called + <code>org.openide.filesystems.FileObject</code> that + provides uniform access to such resources and is the preferred + way that should be used. But of course there can be situations when + this is not suitable. + </hint> + </question> +--> + <answer id="resources-file"> + <p> + XXX no answer for resources-file + </p> + </answer> + + + +<!-- + <question id="resources-layer" when="final"> + Does your module provide own layer? Does it create any files or + folders in it? What it is trying to communicate by that and with which + components? + + <hint> + NetBeans allows automatic and declarative installation of resources + by module layers. Module register files into appropriate places + and other components use that information to perform their task + (build menu, toolbar, window layout, list of templates, set of + options, etc.). + </hint> + </question> +--> + <answer id="resources-layer"> + <p> + XXX no answer for resources-layer + </p> + </answer> + + + +<!-- + <question id="resources-mask" when="final"> + Does your module mask/hide/override any resources provided by other modules in + their layers? + + <hint> + If you mask a file provided by another module, you probably depend + on that and do not want the other module to (for example) change + the file's name. That module shall thus make that file available as an API + of some stability category. + </hint> + </question> +--> + <answer id="resources-mask"> + <p> + XXX no answer for resources-mask + </p> + </answer> + + + +<!-- + <question id="resources-read" when="final"> + Does your module read any resources from layers? For what purpose? + + <hint> + As this is some kind of intermodule dependency, it is a kind of API. + Please describe it and classify according to + <a href="http://openide.netbeans.org/tutorial/api-design.html#categories"> + common stability categories</a>. + </hint> + </question> +--> + <answer id="resources-read"> + <p> + XXX no answer for resources-read + </p> + </answer> + + + +<!-- + <question id="security-grant" when="final"> + Does your code grant additional rights to some other code? + <hint>Avoid using a class loader that adds extra + permissions to loaded code unless really necessary. + Also note that your API implementation + can also expose unneeded permissions to enemy code by + calling AccessController.doPrivileged().</hint> + </question> +--> + <answer id="security-grant"> + <p> + XXX no answer for security-grant + </p> + </answer> + + + +<!-- + <question id="security-policy" when="final"> + Does your functionality require modifications to the standard policy file? + <hint>Your code might pass control to third-party code not + coming from trusted domains. This could be code downloaded over the + network or code coming from libraries that are not bundled + with NetBeans. Which permissions need to be granted to which domains?</hint> + </question> +--> + <answer id="security-policy"> + <p> + XXX no answer for security-policy + </p> + </answer> + + + + +<!-- + <question id="resources-preferences" when="final"> + Does your module uses preferences via Preferences API? Does your module use NbPreferences or + or regular JDK Preferences ? Does it read, write or both ? + Does it share preferences with other modules ? If so, then why ? + <hint> + You may use + <api type="export" group="preferences" + name="preference node name" category="private"> + description of individual keys, where it is used, what it + influences, whether the module reads/write it, etc. + </api> + Due to XML ID restrictions, rather than /org/netbeans/modules/foo give the "name" as org.netbeans.modules.foo. + Note that if you use NbPreferences this name will then be the same as the code name base of the module. + </hint> + </question> +--> + <answer id="resources-preferences"> + <p> + XXX no answer for resources-preferences + </p> + </answer> + +</api-answers> diff --git a/ide/api.lsp/manifest.mf b/ide/api.lsp/manifest.mf index 087bc50..b913ea4 100644 --- a/ide/api.lsp/manifest.mf +++ b/ide/api.lsp/manifest.mf @@ -1,6 +1,6 @@ Manifest-Version: 1.0 OpenIDE-Module: org.netbeans.api.lsp/1 OpenIDE-Module-Localizing-Bundle: org/netbeans/api/lsp/Bundle.properties -OpenIDE-Module-Specification-Version: 1.1 +OpenIDE-Module-Specification-Version: 1.2 AutoUpdate-Show-In-Client: false diff --git a/ide/api.lsp/nbproject/project.properties b/ide/api.lsp/nbproject/project.properties index 445f2ff..774c790 100644 --- a/ide/api.lsp/nbproject/project.properties +++ b/ide/api.lsp/nbproject/project.properties @@ -17,4 +17,7 @@ is.autoload=true javac.source=1.8 javac.compilerargs=-Xlint -Xlint:-serial +javadoc.name=LSP APIs +javadoc.title=LSP APIs +javadoc.arch=${basedir}/arch.xml javadoc.apichanges=${basedir}/apichanges.xml \ No newline at end of file diff --git a/ide/api.lsp/nbproject/project.xml b/ide/api.lsp/nbproject/project.xml index 3d8cea7..33244b3 100644 --- a/ide/api.lsp/nbproject/project.xml +++ b/ide/api.lsp/nbproject/project.xml @@ -90,10 +90,20 @@ <test/> </test-dependency> <test-dependency> + <code-name-base>org.netbeans.modules.masterfs</code-name-base> + <compile-dependency/> + <test/> + </test-dependency> + <test-dependency> <code-name-base>org.netbeans.modules.nbjunit</code-name-base> <recursive/> <compile-dependency/> </test-dependency> + <test-dependency> + <code-name-base>org.openide.util.lookup</code-name-base> + <compile-dependency/> + <test/> + </test-dependency> </test-type> </test-dependencies> <public-packages> diff --git a/ide/api.lsp/src/org/netbeans/api/lsp/Hover.java b/ide/api.lsp/src/org/netbeans/api/lsp/Hover.java new file mode 100644 index 0000000..2d928b8 --- /dev/null +++ b/ide/api.lsp/src/org/netbeans/api/lsp/Hover.java @@ -0,0 +1,67 @@ +/* + * 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.api.lsp; + +import java.util.concurrent.CompletableFuture; +import javax.swing.text.Document; +import org.netbeans.api.annotations.common.NonNull; +import org.netbeans.api.editor.mimelookup.MimeLookup; +import org.netbeans.api.editor.mimelookup.MimePath; +import org.netbeans.lib.editor.util.swing.DocumentUtilities; +import org.netbeans.spi.lsp.HoverProvider; + +/** + * Represents a hover information at a given text document position. + * + * @author Dusan Balek + * @since 1.2 + */ +public final class Hover { + + /** + * Resolves a hover information at the given document offset and returns its + * content. Example usage can be illustrated by: + * {@codesnippet HoverTest#testHoverContent} + * + * @param doc document on which to operate. + * @param offset offset within document + * @return a HTML formatted content + * + * @since 1.2 + */ + @NonNull + public static CompletableFuture<String> getContent(@NonNull Document doc, int offset) { + MimePath mimePath = MimePath.parse(DocumentUtilities.getMimeType(doc)); + CompletableFuture<String>[] futures = MimeLookup.getLookup(mimePath).lookupAll(HoverProvider.class).stream() + .map(provider -> provider.getHoverContent(doc, offset)).toArray(CompletableFuture[]::new); + return CompletableFuture.allOf(futures).thenApply(value -> { + StringBuilder sb = new StringBuilder(); + for (CompletableFuture<String> future : futures) { + String content = future.getNow(null); + if (content != null) { + if (sb.length() > 0) { + sb.append("<p>"); + } + sb.append(content); + } + } + return sb.length() > 0 ? sb.toString() : null; + }); + } +} diff --git a/ide/api.lsp/src/org/netbeans/api/lsp/HyperlinkLocation.java b/ide/api.lsp/src/org/netbeans/api/lsp/HyperlinkLocation.java index 4ded8d2..2069379 100644 --- a/ide/api.lsp/src/org/netbeans/api/lsp/HyperlinkLocation.java +++ b/ide/api.lsp/src/org/netbeans/api/lsp/HyperlinkLocation.java @@ -134,7 +134,8 @@ public final class HyperlinkLocation { /** * Resolves a hyperlink at the given document offset and returns its target - * location(s). + * location(s). Example usage can be illustrated by: + * {@codesnippet HyperlinkLocationTest#testHyperlinkResolve} * * @param doc document on which to operate. * @param offset offset within document @@ -161,7 +162,8 @@ public final class HyperlinkLocation { /** * Resolves a hyperlink at the given document offset and returns its target - * type definition location(s). + * type definition location(s). Example usage can be illustrated by: + * {@codesnippet HyperlinkLocationTest#testHyperlinkTypeDefResolve} * * @param doc document on which to operate. * @param offset offset within document diff --git a/ide/api.lsp/src/org/netbeans/spi/lsp/HoverProvider.java b/ide/api.lsp/src/org/netbeans/spi/lsp/HoverProvider.java new file mode 100644 index 0000000..c22390f --- /dev/null +++ b/ide/api.lsp/src/org/netbeans/spi/lsp/HoverProvider.java @@ -0,0 +1,56 @@ +/* + * 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.spi.lsp; + +import java.util.concurrent.CompletableFuture; +import javax.swing.text.Document; +import org.netbeans.api.annotations.common.NonNull; +import org.netbeans.spi.editor.mimelookup.MimeLocation; + +/** + * Interface for resolving hover information at a given document position. + * Implementations of the interface should be registered in MimeLookup. + * <pre> + * + * {@code @MimeRegistration(mimeType = "text/foo", service = HoverProvider.class) + * public class FooHoverProvider implements HoverProvider { + * ... + * } + * } + * </pre> + * + * @author Dusan Balek + * @since 1.2 + */ +@MimeLocation(subfolderName = "HoverProviders") +public interface HoverProvider { + + /** + * Resolves a hover information at the given document offset and returns its + * content. + * + * @param doc document on which to operate. + * @param offset offset within document + * @return a HTML formatted content + * + * @since 1.2 + */ + @NonNull + CompletableFuture<String> getHoverContent(@NonNull Document doc, int offset); +} diff --git a/ide/api.lsp/test/unit/src/org/netbeans/api/lsp/HoverTest.java b/ide/api.lsp/test/unit/src/org/netbeans/api/lsp/HoverTest.java new file mode 100644 index 0000000..3d2dd5a --- /dev/null +++ b/ide/api.lsp/test/unit/src/org/netbeans/api/lsp/HoverTest.java @@ -0,0 +1,80 @@ +/* + * 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.api.lsp; + +import java.util.concurrent.CompletableFuture; +import javax.swing.text.BadLocationException; +import javax.swing.text.DefaultStyledDocument; +import javax.swing.text.Document; +import org.netbeans.api.editor.mimelookup.MimePath; +import org.netbeans.api.editor.mimelookup.test.MockMimeLookup; +import org.netbeans.junit.NbTestCase; +import org.netbeans.spi.lsp.HoverProvider; + +/** + * + * @author Dusan Balek + */ +public class HoverTest extends NbTestCase { + + public HoverTest(String name) { + super(name); + } + + @Override + public void setUp () throws Exception { + super.setUp(); + clearWorkDir(); + MockMimeLookup.setInstances (MimePath.get ("text/foo"), new FooHoverProvider()); + } + + public void testHoverContent() { + Document doc = createDocument("text/foo", ""); + int offset = 0; + // BEGIN: HoverTest#testHoverContent + + // Resolve a hover information at the given document offset... + CompletableFuture<String> future = Hover.getContent(doc, offset); + + // ...and get its content + String content = future.getNow(null); + assertEquals("Foo hover information", content); + + // END: HoverTest#testHoverContent + } + + private Document createDocument(String mimeType, String contents) { + Document doc = new DefaultStyledDocument(); + doc.putProperty("mimeType", mimeType); + try { + doc.insertString(0, contents, null); + return doc; + } catch (BadLocationException ble) { + throw new IllegalStateException(ble); + } + } + + private static class FooHoverProvider implements HoverProvider { + + @Override + public CompletableFuture<String> getHoverContent(Document doc, int offset) { + return CompletableFuture.completedFuture("Foo hover information"); + } + } +} diff --git a/ide/api.lsp/test/unit/src/org/netbeans/api/lsp/HyperlinkLocationTest.java b/ide/api.lsp/test/unit/src/org/netbeans/api/lsp/HyperlinkLocationTest.java new file mode 100644 index 0000000..7c07cc9 --- /dev/null +++ b/ide/api.lsp/test/unit/src/org/netbeans/api/lsp/HyperlinkLocationTest.java @@ -0,0 +1,136 @@ +/* + * 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.api.lsp; + +import java.util.List; +import java.util.concurrent.CompletableFuture; +import javax.swing.text.BadLocationException; +import javax.swing.text.DefaultStyledDocument; +import javax.swing.text.Document; +import org.netbeans.api.editor.mimelookup.MimePath; +import org.netbeans.api.editor.mimelookup.test.MockMimeLookup; +import org.netbeans.junit.NbTestCase; +import org.netbeans.spi.lsp.HyperlinkLocationProvider; +import org.netbeans.spi.lsp.HyperlinkTypeDefLocationProvider; +import org.openide.filesystems.FileObject; +import org.openide.filesystems.FileUtil; + +/** + * + * @author Dusan Balek + */ +public class HyperlinkLocationTest extends NbTestCase { + + private FileObject srcFile; + + public HyperlinkLocationTest(String name) { + super(name); + } + + @Override + public void setUp () throws Exception { + super.setUp(); + clearWorkDir(); + FileObject wd = FileUtil.toFileObject(getWorkDir()); + srcFile = FileUtil.createData(wd, "bar.test"); + MockMimeLookup.setInstances (MimePath.get ("text/foo"), new FooLocationProvider()); + } + + public void testHyperlinkResolve() { + Document doc = createDocument("text/foo", ""); + int offset = 0; + // BEGIN: HyperlinkLocationTest#testHyperlinkResolve + + // Resolve a hyperlink at the given document offset... + CompletableFuture<List<HyperlinkLocation>> future = HyperlinkLocation.resolve(doc, offset); + + // ...and get its target location(s) + List<HyperlinkLocation> locations = future.getNow(null); + assertNotNull(locations); + assertEquals(1, locations.size()); + HyperlinkLocation location = locations.get(0); + + // get location's file object + FileObject fileObject = location.getFileObject(); + assertEquals(srcFile, fileObject); + + // get location's start offset + int startOffset = location.getStartOffset(); + assertEquals(10, startOffset); + + // get location's end offset + int endOffset = location.getEndOffset(); + assertEquals(20, endOffset); + + // END: HyperlinkLocationTest#testHyperlinkResolve + } + + public void testHyperlinkTypeDefResolve() { + Document doc = createDocument("text/foo", ""); + int offset = 0; + // BEGIN: HyperlinkLocationTest#testHyperlinkTypeDefResolve + + // Resolve a hyperlink at the given document offset... + CompletableFuture<List<HyperlinkLocation>> future = HyperlinkLocation.resolveTypeDefinition(doc, offset); + + // ...and get its target type definition location(s) + List<HyperlinkLocation> locations = future.getNow(null); + assertNotNull(locations); + assertEquals(1, locations.size()); + HyperlinkLocation location = locations.get(0); + + // get location's file object + FileObject fileObject = location.getFileObject(); + assertEquals(srcFile, fileObject); + + // get location's start offset + int startOffset = location.getStartOffset(); + assertEquals(10, startOffset); + + // get location's end offset + int endOffset = location.getEndOffset(); + assertEquals(20, endOffset); + + // END: HyperlinkLocationTest#testHyperlinkTypeDefResolve + } + + private Document createDocument(String mimeType, String contents) { + Document doc = new DefaultStyledDocument(); + doc.putProperty("mimeType", mimeType); + try { + doc.insertString(0, contents, null); + return doc; + } catch (BadLocationException ble) { + throw new IllegalStateException(ble); + } + } + + private class FooLocationProvider implements HyperlinkLocationProvider, HyperlinkTypeDefLocationProvider { + + @Override + public CompletableFuture<HyperlinkLocation> getHyperlinkLocation(Document doc, int offset) { + return CompletableFuture.completedFuture(HyperlinkLocationProvider.createHyperlinkLocation(srcFile, 10, 20)); + } + + @Override + public CompletableFuture<HyperlinkLocation> getHyperlinkTypeDefLocation(Document doc, int offset) { + return CompletableFuture.completedFuture(HyperlinkLocationProvider.createHyperlinkLocation(srcFile, 10, 20)); + } + } +} diff --git a/java/java.editor/nbproject/project.xml b/java/java.editor/nbproject/project.xml index 460fd94..01f6757 100644 --- a/java/java.editor/nbproject/project.xml +++ b/java/java.editor/nbproject/project.xml @@ -58,7 +58,7 @@ <compile-dependency/> <run-dependency> <release-version>1</release-version> - <specification-version>1.1</specification-version> + <specification-version>1.2</specification-version> </run-dependency> </dependency> <dependency> diff --git a/java/java.editor/src/org/netbeans/modules/editor/java/JavaHoverProvider.java b/java/java.editor/src/org/netbeans/modules/editor/java/JavaHoverProvider.java new file mode 100644 index 0000000..31ee21e --- /dev/null +++ b/java/java.editor/src/org/netbeans/modules/editor/java/JavaHoverProvider.java @@ -0,0 +1,65 @@ +/* + * 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.editor.java; + +import java.util.Collections; +import java.util.concurrent.Callable; +import java.util.concurrent.CompletableFuture; +import javax.lang.model.element.Element; +import javax.swing.text.Document; +import org.netbeans.api.editor.mimelookup.MimeRegistration; +import org.netbeans.api.java.source.CompilationInfo; +import org.netbeans.api.java.source.ui.ElementJavadoc; +import org.netbeans.modules.java.completion.JavaDocumentationTask; +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.HoverProvider; + +/** + * + * @author Dusan Balek + */ +@MimeRegistration(mimeType = "text/x-java", service = HoverProvider.class) +public class JavaHoverProvider implements HoverProvider { + + @Override + public CompletableFuture<String> getHoverContent(Document doc, int offset) { + try { + JavaDocumentationTask<CompletableFuture<String>> task = JavaDocumentationTask.create(offset, null, new JavaDocumentationTask.DocumentationFactory<CompletableFuture<String>>() { + @Override + public CompletableFuture<String> create(CompilationInfo compilationInfo, Element element, Callable<Boolean> cancel) { + return (CompletableFuture<String>) 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); + } + }); + CompletableFuture<String> documentation = task.getDocumentation(); + return documentation != null ? documentation : CompletableFuture.completedFuture(null); + } catch (ParseException parseException) { + return CompletableFuture.completedFuture(null); + } + } +} diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/TextDocumentServiceImpl.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/TextDocumentServiceImpl.java index 42ae0ad..e492629 100644 --- a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/TextDocumentServiceImpl.java +++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/TextDocumentServiceImpl.java @@ -49,11 +49,8 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.concurrent.Callable; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.BiConsumer; @@ -149,7 +146,6 @@ import org.netbeans.api.java.source.Task; import org.netbeans.api.java.source.TreePathHandle; import org.netbeans.api.java.source.TreeUtilities; import org.netbeans.api.java.source.WorkingCopy; -import org.netbeans.api.java.source.ui.ElementJavadoc; import org.netbeans.api.java.source.ui.ElementOpen; import org.netbeans.api.lexer.TokenSequence; import org.netbeans.api.lsp.Completion; @@ -162,7 +158,6 @@ import org.netbeans.modules.editor.java.GoToSupport; import org.netbeans.modules.editor.java.GoToSupport.Context; import org.netbeans.modules.editor.java.GoToSupport.GoToTarget; import org.netbeans.modules.gsf.testrunner.ui.api.TestMethodController.TestMethod; -import org.netbeans.modules.java.completion.JavaDocumentationTask; import org.netbeans.modules.java.editor.base.fold.JavaElementFoldVisitor; import org.netbeans.modules.java.editor.base.fold.JavaElementFoldVisitor.FoldCreator; import org.netbeans.modules.java.editor.base.semantic.MarkOccurrencesHighlighterBase; @@ -450,52 +445,21 @@ public class TextDocumentServiceImpl implements TextDocumentService, LanguageCli if (server.openedProjects().getNow(null) == null) { return CompletableFuture.completedFuture(null); } - try { - String uri = params.getTextDocument().getUri(); - FileObject file = fromURI(uri); - if (file == null) { - return CompletableFuture.completedFuture(null); - } - EditorCookie ec = file.getLookup().lookup(EditorCookie.class); - Document doc = ec.openDocument(); - final JavaDocumentationTask<Future<String>> task = JavaDocumentationTask.create(Utils.getOffset(doc, params.getPosition()), null, 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); - } - }); - Future<String> futureJavadoc = task.getDocumentation(); - CompletableFuture<Hover> result = new CompletableFuture<Hover>() { - @Override - public boolean cancel(boolean mayInterruptIfRunning) { - return futureJavadoc != null && futureJavadoc.cancel(mayInterruptIfRunning) && super.cancel(mayInterruptIfRunning); - } - }; - JAVADOC_WORKER.post(() -> { - try { - String javadoc = futureJavadoc != null ? futureJavadoc.get() : null; - if (javadoc != null) { - MarkupContent markup = new MarkupContent(); - markup.setKind("markdown"); - markup.setValue(html2MD(javadoc)); - result.complete(new Hover(markup)); - } else { - result.complete(null); - } - } catch (ExecutionException | InterruptedException ex) { - result.completeExceptionally(ex); - } - }); - return result; - } catch (IOException | ParseException ex) { - throw new IllegalStateException(ex); + String uri = params.getTextDocument().getUri(); + FileObject file = fromURI(uri); + Document doc = openedDocuments.get(uri); + if (file == null || doc == null) { + return CompletableFuture.completedFuture(null); } + return org.netbeans.api.lsp.Hover.getContent(doc, Utils.getOffset(doc, params.getPosition())).thenApply(content -> { + if (content != null) { + MarkupContent markup = new MarkupContent(); + markup.setKind("markdown"); + markup.setValue(html2MD(content)); + return new Hover(markup); + } + return null; + }); } @Override 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 159d74a..075782b 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 @@ -1240,6 +1240,7 @@ public class ServerTest extends NbTestCase { LanguageServer server = serverLauncher.getRemoteProxy(); InitializeResult result = server.initialize(new InitializeParams()).get(); assertTrue(result.getCapabilities().getHoverProvider()); + server.getTextDocumentService().didOpen(new DidOpenTextDocumentParams(new TextDocumentItem(toURI(src), "java", 0, code))); Hover hover = server.getTextDocumentService().hover(new HoverParams(new TextDocumentIdentifier(toURI(src)), new Position(5, 10))).get(); assertNotNull(hover); assertTrue(hover.getContents().isRight()); diff --git a/java/java.sourceui/src/org/netbeans/api/java/source/ui/ElementJavadoc.java b/java/java.sourceui/src/org/netbeans/api/java/source/ui/ElementJavadoc.java index 9d06ecd..15a68e6 100644 --- a/java/java.sourceui/src/org/netbeans/api/java/source/ui/ElementJavadoc.java +++ b/java/java.sourceui/src/org/netbeans/api/java/source/ui/ElementJavadoc.java @@ -65,8 +65,8 @@ import javax.swing.Action; import java.net.MalformedURLException; import java.util.ArrayList; import java.util.concurrent.Callable; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.CountDownLatch; -import java.util.concurrent.FutureTask; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Function; import javax.lang.model.element.AnnotationMirror; @@ -131,7 +131,7 @@ public class ElementJavadoc { private final FileObject fileObject; private final ElementHandle<? extends Element> handle; //private Doc doc; - private volatile Future<String> content; + private volatile CompletableFuture<String> content; private final Callable<Boolean> cancel; private Map<String, ElementHandle<? extends Element>> links = new HashMap<>(); private int linkCounter = 0; @@ -371,26 +371,30 @@ public class ElementJavadoc { computeDocURL(Collections.emptyList(), true, cancel); doc = header.append(noJavadocFound()); } - this.content = new Now(doc.toString()); + this.content = CompletableFuture.completedFuture(doc.toString()); } catch (JavadocHelper.RemoteJavadocException re) { if (fileObject == null || JavaSource.forFileObject(fileObject) == null) { header.append(noJavadocFound()); - this.content = new Now(header.toString()); + this.content = CompletableFuture.completedFuture(header.toString()); return; } - this.content = new FutureTask<>(() -> { - final JavaSourceUtil.Handle ch = JavaSourceUtil.createControllerHandle(fileObject, null); - final CompilationController c = (CompilationController) ch.getCompilationController(); - c.toPhase(Phase.RESOLVED); - final Element el = handle.resolve(c); - CharSequence doc = getElementDoc(el, c, header, url, false); - if (doc == null) { - computeDocURL(Collections.emptyList(), false, cancel); - doc = header.append(noJavadocFound()); + this.content = CompletableFuture.supplyAsync(() -> { + try { + final JavaSourceUtil.Handle ch = JavaSourceUtil.createControllerHandle(fileObject, null); + final CompilationController c = (CompilationController) ch.getCompilationController(); + c.toPhase(Phase.RESOLVED); + final Element el = handle.resolve(c); + CharSequence doc = getElementDoc(el, c, header, url, false); + if (doc == null) { + computeDocURL(Collections.emptyList(), false, cancel); + doc = header.append(noJavadocFound()); + } + return doc.toString(); + } catch (Exception ex) { + Exceptions.printStackTrace(ex); + return null; } - return doc.toString(); - }); - RP.post((Runnable)this.content); + }, RP); } } @@ -406,7 +410,7 @@ public class ElementJavadoc { private ElementJavadoc(final String message, final Callable<Boolean> cancel) { assert message != null; - this.content = new Now(message); + this.content = CompletableFuture.completedFuture(message); this.docURL = null; this.handle = null; this.cpInfo = null; @@ -1537,40 +1541,6 @@ public class ElementJavadoc { throw (T) t; } - private static final class Now implements Future<String> { - - private final String value; - - Now(final String value) { - this.value = value; - } - - @Override - public boolean cancel(boolean mayInterruptIfRunning) { - return false; - } - - @Override - public boolean isCancelled() { - return false; - } - - @Override - public boolean isDone() { - return true; - } - - @Override - public String get() throws InterruptedException, ExecutionException { - return value; - } - - @Override - public String get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { - return value; - } - } - private void assignSource( @NonNull final Element element, @NonNull final CompilationInfo compilationInfo, diff --git a/nbbuild/build.properties b/nbbuild/build.properties index 759f93a..419f17f 100644 --- a/nbbuild/build.properties +++ b/nbbuild/build.properties @@ -78,6 +78,7 @@ config.javadoc.stable=\ api.intent,\ api.io,\ api.knockout,\ + api.lsp,\ api.maven,\ api.scripting,\ libs.graalsdk,\ diff --git a/nbbuild/javadoctools/links.xml b/nbbuild/javadoctools/links.xml index 5d07aaa..e6e8f21 100644 --- a/nbbuild/javadoctools/links.xml +++ b/nbbuild/javadoctools/links.xml @@ -241,3 +241,4 @@ <link href="${javadoc.docs.org-netbeans-modules-web-common}" offline="true" packagelistloc="${netbeans.javadoc.dir}/org-netbeans-modules-web-common"/> <link href="${javadoc.docs.org-netbeans-modules-db-core}" offline="true" packagelistloc="${netbeans.javadoc.dir}/org-netbeans-modules-db-core"/> <link href="${javadoc.docs.org-netbeans-modules-nativeimage-api}" offline="true" packagelistloc="${netbeans.javadoc.dir}/org-netbeans-modules-nativeimage-api"/> +<link href="${javadoc.docs.org-netbeans-api-lsp}" offline="true" packagelistloc="${netbeans.javadoc.dir}/org-netbeans-api-lsp"/> diff --git a/nbbuild/javadoctools/properties.xml b/nbbuild/javadoctools/properties.xml index 3c633d1..f85ccc1 100644 --- a/nbbuild/javadoctools/properties.xml +++ b/nbbuild/javadoctools/properties.xml @@ -238,3 +238,4 @@ <property name="javadoc.docs.org-netbeans-modules-web-common" value="${javadoc.web.root}/org-netbeans-modules-web-common"/> <property name="javadoc.docs.org-netbeans-modules-db-core" value="${javadoc.web.root}/org-netbeans-modules-db-core"/> <property name="javadoc.docs.org-netbeans-modules-nativeimage-api" value="${javadoc.web.root}/org-netbeans-modules-nativeimage-api"/> +<property name="javadoc.docs.org-netbeans-api-lsp" value="${javadoc.web.root}/org-netbeans-api-lsp"/> diff --git a/nbbuild/javadoctools/replaces.xml b/nbbuild/javadoctools/replaces.xml index f7e27e8..d0ec100 100644 --- a/nbbuild/javadoctools/replaces.xml +++ b/nbbuild/javadoctools/replaces.xml @@ -238,3 +238,4 @@ <replacefilter token="@org-netbeans-modules-web-common@" value="${javadoc.docs.org-netbeans-modules-web-common}"/> <replacefilter token="@org-netbeans-modules-db-core@" value="${javadoc.docs.org-netbeans-modules-db-core}"/> <replacefilter token="@org-netbeans-modules-nativeimage-api@" value="${javadoc.docs.org-netbeans-modules-nativeimage-api}"/> +<replacefilter token="@org-netbeans-api-lsp@" value="${javadoc.docs.org-netbeans-api-lsp}"/> --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@netbeans.apache.org For additional commands, e-mail: commits-h...@netbeans.apache.org For further information about the NetBeans mailing lists, visit: https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists