You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@netbeans.apache.org by db...@apache.org on 2021/03/26 07:55:24 UTC
[netbeans] branch master updated: Micronaut support (#2812)
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 3bf5558 Micronaut support (#2812)
3bf5558 is described below
commit 3bf55585368d30191e080649fbfa846183d5c005
Author: Dusan Balek <du...@oracle.com>
AuthorDate: Fri Mar 26 08:55:07 2021 +0100
Micronaut support (#2812)
* Micronaut support module added.
* Yaml parser enhanced with error recovery.
* JCodings library wapper module created to prevent overlaps.
* LSP: Navigation between Micronaut config yaml files and Java sources.
* Code completion in Micronaut config yaml files available via LSP.
---
enterprise/micronaut/build.xml | 25 +
.../android-json-0.0.20131108.vaadin1-license.txt | 209 +++++
.../micronaut/external/binaries-list | 6 +-
...g-boot-configuration-metadata-2.4.4-license.txt | 210 +++++
enterprise/micronaut/manifest.mf | 7 +
.../micronaut}/nbproject/project.properties | 15 +-
.../micronaut}/nbproject/project.xml | 202 +++--
.../netbeans/modules/micronaut/Bundle.properties | 5 +-
.../micronaut/MicronautConfigProperties.java | 90 +++
.../micronaut/MicronautConfigUtilities.java | 220 ++++++
.../MicronautConfigCompletionCollector.java | 101 +++
.../completion/MicronautConfigCompletionItem.java | 302 ++++++++
.../MicronautConfigCompletionProvider.java | 117 +++
.../completion/MicronautConfigCompletionTask.java | 207 +++++
.../completion/MicronautConfigDocumentation.java | 104 +++
.../MicronautJavaConfigPropertiesCompletion.java | 140 ++++
.../MicronautConfigHyperlinkProvider.java | 152 ++++
.../hyperlink/MicronautJavaHyperlinkProvider.java | 205 +++++
.../micronaut/newproject/BasePropertiesVisual.form | 348 +++++++++
.../micronaut/newproject/BasePropertiesVisual.java | 424 ++++++++++
.../newproject/BasePropertiesWizardPanel.java | 108 +++
.../modules/micronaut/newproject/Bundle.properties | 27 +-
.../micronaut/newproject/FeatureTogglePanel.form | 78 ++
.../micronaut/newproject/FeatureTogglePanel.java | 89 +++
.../micronaut/newproject/FeaturesVisual.form | 64 ++
.../micronaut/newproject/FeaturesVisual.java | 190 +++++
.../micronaut/newproject/FeaturesWizardPanel.java | 109 +++
.../MicronautGradleProjectDescription.html | 29 +
.../newproject/MicronautLaunchService.java | 174 +++++
.../MicronautMavenProjectDescription.html | 29 +
.../newproject/MicronautProjectWizardIterator.java | 224 ++++++
.../newproject/NameAndLocationVisual.form | 141 ++++
.../newproject/NameAndLocationVisual.java | 273 +++++++
.../newproject/NameAndLocationWizardPanel.java | 108 +++
.../refactor/MicronautRefactoringFactory.java | 243 ++++++
.../netbeans/modules/micronaut/resources/layer.xml | 33 +
.../modules/micronaut/resources/micronaut.png | Bin 0 -> 503 bytes
ide/api.lsp/build.xml | 25 +
ide/api.lsp/manifest.mf | 6 +
.../nbproject/project.properties} | 5 +-
ide/api.lsp/nbproject/project.xml | 105 +++
.../src/org/netbeans/api/lsp/Bundle.properties} | 7 +-
.../src/org/netbeans/api/lsp/Completion.java | 392 ++++++++++
.../org/netbeans/api/lsp/HyperlinkLocation.java | 160 ++++
ide/api.lsp/src/org/netbeans/api/lsp/TextEdit.java | 70 ++
.../netbeans/modules/lsp/CompletionAccessor.java | 60 ++
.../modules/lsp/HyperlinkLocationAccessor.java | 53 ++
.../org/netbeans/spi/lsp/CompletionCollector.java | 358 +++++++++
.../spi/lsp/HyperlinkLocationProvider.java | 72 ++
.../src/org/netbeans/api/lsp/CompletionTest.java | 131 ++++
.../csl/editor/completion/GsfCompletionDoc.java | 7 +-
.../editor/completion/GsfCompletionProvider.java | 2 +-
.../modules/languages/yaml/YamlCompletion.java | 18 +-
.../modules/languages/yaml/YamlParser.java | 125 +--
ide/libs.bytelist/external/binaries-list | 3 +-
ide/libs.bytelist/nbproject/project.properties | 3 +-
ide/libs.bytelist/nbproject/project.xml | 14 +-
ide/libs.jcodings/build.xml | 24 +
.../external/binaries-list | 1 -
.../external/jcodings-1.0.18-license.txt | 0
ide/libs.jcodings/manifest.mf | 4 +
.../nbproject/project.properties | 2 -
.../nbproject/project.xml | 20 +-
.../org/netbeans/libs/jcodings/Bundle.properties} | 9 +-
ide/textmate.lexer/external/binaries-list | 1 -
.../external/jcodings-1.0.18-license.txt | 23 -
ide/textmate.lexer/nbproject/project.properties | 3 +-
ide/textmate.lexer/nbproject/project.xml | 11 +-
java/java.editor/nbproject/project.xml | 11 +-
.../netbeans/modules/editor/java/GoToSupport.java | 55 +-
.../editor/java/JavaCompletionCollector.java | 548 +++++++++++++
.../editor/hyperlink/JavaHyperlinkProvider.java | 20 +-
.../nbcode/nbproject/platform.properties | 113 ++-
java/java.lsp.server/nbproject/project.xml | 23 +-
java/java.lsp.server/script/etc/nbcode.clusters | 1 +
.../server/protocol/TextDocumentServiceImpl.java | 855 ++++++---------------
java/java.lsp.server/vscode/src/extension.ts | 2 +-
java/java.sourceui/apichanges.xml | 12 +
java/java.sourceui/nbproject/project.properties | 2 +-
java/java.sourceui/nbproject/project.xml | 9 +
.../netbeans/api/java/source/ui/ElementOpen.java | 119 +++
.../java/plugins/JavaWhereUsedQueryPlugin.java | 4 +-
.../org/netbeans/nbbuild/extlibs/ignored-overlaps | 3 -
nbbuild/cluster.properties | 3 +
84 files changed, 7572 insertions(+), 930 deletions(-)
diff --git a/enterprise/micronaut/build.xml b/enterprise/micronaut/build.xml
new file mode 100644
index 0000000..90188c2
--- /dev/null
+++ b/enterprise/micronaut/build.xml
@@ -0,0 +1,25 @@
+<?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.
+
+-->
+<project basedir="." default="netbeans" name="enterprise/micronaut">
+ <description>Builds, tests, and runs the project org.netbeans.modules.micronaut</description>
+ <import file="../../nbbuild/templates/projectized.xml"/>
+</project>
diff --git a/enterprise/micronaut/external/android-json-0.0.20131108.vaadin1-license.txt b/enterprise/micronaut/external/android-json-0.0.20131108.vaadin1-license.txt
new file mode 100644
index 0000000..e24e220
--- /dev/null
+++ b/enterprise/micronaut/external/android-json-0.0.20131108.vaadin1-license.txt
@@ -0,0 +1,209 @@
+Name: JSON library from Android SDK
+Description: JSON (JavaScript Object Notation) is a lightweight data-interchange format. This is the org.json compatible Android implementation extracted from the Android SDK
+Version: 0.0.20131108.vaadin1
+License: Apache-2.0
+Origin: Google
+Source: http://developer.android.com/sdk/
+
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed 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.
diff --git a/ide/libs.bytelist/nbproject/project.properties b/enterprise/micronaut/external/binaries-list
similarity index 79%
copy from ide/libs.bytelist/nbproject/project.properties
copy to enterprise/micronaut/external/binaries-list
index 20a62cb..52d00e4 100644
--- a/ide/libs.bytelist/nbproject/project.properties
+++ b/enterprise/micronaut/external/binaries-list
@@ -15,7 +15,5 @@
# specific language governing permissions and limitations
# under the License.
-is.autoload=true
-release.external/bytelist-1.0.15.jar=modules/ext/bytelist-1.0.15.jar
-release.external/jcodings-1.0.18.jar=modules/ext/jcodings-1.0.18.jar
-spec.version.base=0.41.0
+5F7BD843949ED9DEF4A13632D11C6ABE4EDBBE09 org.springframework.boot:spring-boot-configuration-metadata:2.4.4
+FA26D351FE62A6A17F5CDA1287C1C6110DEC413F com.vaadin.external.google:android-json:0.0.20131108.vaadin1
\ No newline at end of file
diff --git a/enterprise/micronaut/external/spring-boot-configuration-metadata-2.4.4-license.txt b/enterprise/micronaut/external/spring-boot-configuration-metadata-2.4.4-license.txt
new file mode 100644
index 0000000..e3c448f
--- /dev/null
+++ b/enterprise/micronaut/external/spring-boot-configuration-metadata-2.4.4-license.txt
@@ -0,0 +1,210 @@
+Name: Spring Boot Configuration Metadata
+Description: Spring Boot Configuration Metadata library
+Version: 2.4.4
+License: Apache-2.0
+Origin: Pivotal Software, Inc.
+URL: https://spring.io/projects/spring-boot
+Source: https://github.com/spring-projects/spring-boot
+
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed 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.
diff --git a/enterprise/micronaut/manifest.mf b/enterprise/micronaut/manifest.mf
new file mode 100644
index 0000000..cec26e3
--- /dev/null
+++ b/enterprise/micronaut/manifest.mf
@@ -0,0 +1,7 @@
+Manifest-Version: 1.0
+AutoUpdate-Show-In-Client: true
+OpenIDE-Module: org.netbeans.modules.micronaut
+OpenIDE-Module-Layer: org/netbeans/modules/micronaut/resources/layer.xml
+OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/micronaut/Bundle.properties
+OpenIDE-Module-Implementation-Version: 1
+
diff --git a/java/java.sourceui/nbproject/project.properties b/enterprise/micronaut/nbproject/project.properties
similarity index 77%
copy from java/java.sourceui/nbproject/project.properties
copy to enterprise/micronaut/nbproject/project.properties
index 01b9ec4..037c5e3 100644
--- a/java/java.sourceui/nbproject/project.properties
+++ b/enterprise/micronaut/nbproject/project.properties
@@ -14,14 +14,9 @@
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
-javadoc.apichanges=${basedir}/apichanges.xml
-javac.compilerargs=-Xlint -Xlint:-serial
-javac.source=1.8
-javadoc.arch=${basedir}/arch.xml
-spec.version.base=1.57.0
-
-test.config.stableBTD.includes=**/*Test.class
-test.config.stableBTD.excludes=\
- **/ElementHeadersTest.class
-requires.nb.javac=true
+javac.source=1.8
+javac.compilerargs=-Xlint -Xlint:-serial
+release.external/spring-boot-configuration-metadata-2.4.4.jar=modules/ext/spring-boot-configuration-metadata-2.4.4.jar
+release.external/android-json-0.0.20131108.vaadin1.jar=modules/ext/android-json-0.0.20131108.vaadin1.jar
+spec.version.base=1.0
diff --git a/java/java.sourceui/nbproject/project.xml b/enterprise/micronaut/nbproject/project.xml
similarity index 66%
copy from java/java.sourceui/nbproject/project.xml
copy to enterprise/micronaut/nbproject/project.xml
index 33f9b27..e15395e 100644
--- a/java/java.sourceui/nbproject/project.xml
+++ b/enterprise/micronaut/nbproject/project.xml
@@ -23,28 +23,27 @@
<type>org.netbeans.modules.apisupport.project</type>
<configuration>
<data xmlns="http://www.netbeans.org/ns/nb-module-project/3">
- <code-name-base>org.netbeans.modules.java.sourceui</code-name-base>
+ <code-name-base>org.netbeans.modules.micronaut</code-name-base>
<module-dependencies>
<dependency>
- <code-name-base>org.netbeans.api.annotations.common</code-name-base>
+ <code-name-base>com.google.gson</code-name-base>
<build-prerequisite/>
<compile-dependency/>
<run-dependency>
- <release-version>1</release-version>
- <specification-version>1.10</specification-version>
+ <specification-version>2.7</specification-version>
</run-dependency>
</dependency>
<dependency>
- <code-name-base>org.netbeans.api.java</code-name-base>
+ <code-name-base>org.netbeans.api.java.classpath</code-name-base>
<build-prerequisite/>
<compile-dependency/>
<run-dependency>
<release-version>1</release-version>
- <specification-version>1.35</specification-version>
+ <specification-version>1.65</specification-version>
</run-dependency>
</dependency>
<dependency>
- <code-name-base>org.netbeans.api.java.classpath</code-name-base>
+ <code-name-base>org.netbeans.api.lsp</code-name-base>
<build-prerequisite/>
<compile-dependency/>
<run-dependency>
@@ -58,15 +57,15 @@
<compile-dependency/>
<run-dependency>
<release-version>1</release-version>
- <specification-version>1.40</specification-version>
+ <specification-version>1.59</specification-version>
</run-dependency>
</dependency>
<dependency>
- <code-name-base>org.netbeans.api.progress.nb</code-name-base>
+ <code-name-base>org.netbeans.api.templates</code-name-base>
<build-prerequisite/>
<compile-dependency/>
<run-dependency>
- <specification-version>1.40</specification-version>
+ <specification-version>1.19</specification-version>
</run-dependency>
</dependency>
<dependency>
@@ -74,77 +73,96 @@
<build-prerequisite/>
<compile-dependency/>
<run-dependency>
- <specification-version>8.19</specification-version>
+ <specification-version>8.33</specification-version>
</run-dependency>
</dependency>
<dependency>
- <code-name-base>org.netbeans.libs.javacimpl</code-name-base>
+ <code-name-base>org.netbeans.modules.csl.api</code-name-base>
<build-prerequisite/>
<compile-dependency/>
<run-dependency>
- <release-version>1</release-version>
+ <release-version>2</release-version>
<implementation-version/>
</run-dependency>
</dependency>
<dependency>
- <code-name-base>org.netbeans.libs.lucene</code-name-base>
+ <code-name-base>org.netbeans.modules.csl.types</code-name-base>
<build-prerequisite/>
<compile-dependency/>
<run-dependency>
- <release-version>3</release-version>
- <specification-version>3.0</specification-version>
+ <release-version>1</release-version>
+ <specification-version>1.11</specification-version>
</run-dependency>
</dependency>
<dependency>
- <code-name-base>org.netbeans.modules.editor.fold</code-name-base>
+ <code-name-base>org.netbeans.modules.editor.codetemplates</code-name-base>
<build-prerequisite/>
<compile-dependency/>
<run-dependency>
<release-version>1</release-version>
- <specification-version>1.39</specification-version>
+ <specification-version>1.54</specification-version>
</run-dependency>
</dependency>
<dependency>
- <code-name-base>org.netbeans.modules.editor.lib2</code-name-base>
+ <code-name-base>org.netbeans.modules.editor.completion</code-name-base>
<build-prerequisite/>
<compile-dependency/>
<run-dependency>
<release-version>1</release-version>
- <specification-version>2.0</specification-version>
+ <specification-version>1.56</specification-version>
</run-dependency>
</dependency>
<dependency>
- <code-name-base>org.netbeans.modules.java.lexer</code-name-base>
+ <code-name-base>org.netbeans.modules.editor.document</code-name-base>
<build-prerequisite/>
<compile-dependency/>
<run-dependency>
- <release-version>1</release-version>
- <specification-version>1.2</specification-version>
+ <specification-version>1.20</specification-version>
+ </run-dependency>
+ </dependency>
+ <dependency>
+ <code-name-base>org.netbeans.modules.editor.indent</code-name-base>
+ <build-prerequisite/>
+ <compile-dependency/>
+ <run-dependency>
+ <release-version>2</release-version>
+ <specification-version>1.54</specification-version>
+ </run-dependency>
+ </dependency>
+ <dependency>
+ <code-name-base>org.netbeans.modules.editor.lib</code-name-base>
+ <build-prerequisite/>
+ <compile-dependency/>
+ <run-dependency>
+ <release-version>3</release-version>
+ <specification-version>4.18</specification-version>
</run-dependency>
</dependency>
<dependency>
- <code-name-base>org.netbeans.modules.java.platform</code-name-base>
+ <code-name-base>org.netbeans.modules.editor.mimelookup</code-name-base>
<build-prerequisite/>
<compile-dependency/>
<run-dependency>
<release-version>1</release-version>
- <specification-version>1.9</specification-version>
+ <specification-version>1.51</specification-version>
</run-dependency>
</dependency>
<dependency>
- <code-name-base>org.netbeans.modules.java.preprocessorbridge</code-name-base>
+ <code-name-base>org.netbeans.modules.editor.util</code-name-base>
<build-prerequisite/>
<compile-dependency/>
<run-dependency>
- <specification-version>1.3</specification-version>
+ <release-version>1</release-version>
+ <specification-version>1.76</specification-version>
</run-dependency>
</dependency>
<dependency>
- <code-name-base>org.netbeans.modules.java.source</code-name-base>
+ <code-name-base>org.netbeans.modules.java.project</code-name-base>
<build-prerequisite/>
<compile-dependency/>
<run-dependency>
- <implementation-version/>
+ <release-version>1</release-version>
+ <specification-version>1.83</specification-version>
</run-dependency>
</dependency>
<dependency>
@@ -152,16 +170,16 @@
<build-prerequisite/>
<compile-dependency/>
<run-dependency>
- <implementation-version/>
+ <specification-version>2.49</specification-version>
</run-dependency>
</dependency>
<dependency>
- <code-name-base>org.netbeans.modules.jumpto</code-name-base>
+ <code-name-base>org.netbeans.modules.java.sourceui</code-name-base>
<build-prerequisite/>
<compile-dependency/>
<run-dependency>
<release-version>1</release-version>
- <specification-version>1.50</specification-version>
+ <specification-version>1.58</specification-version>
</run-dependency>
</dependency>
<dependency>
@@ -170,7 +188,7 @@
<compile-dependency/>
<run-dependency>
<release-version>2</release-version>
- <specification-version>1.20</specification-version>
+ <specification-version>1.74</specification-version>
</run-dependency>
</dependency>
<dependency>
@@ -179,42 +197,41 @@
<compile-dependency/>
<run-dependency>
<release-version>1</release-version>
- <specification-version>9.0</specification-version>
+ <specification-version>9.18</specification-version>
</run-dependency>
</dependency>
<dependency>
- <code-name-base>org.netbeans.modules.parsing.indexing</code-name-base>
+ <code-name-base>org.netbeans.modules.projectapi</code-name-base>
<build-prerequisite/>
<compile-dependency/>
<run-dependency>
- <specification-version>9.0</specification-version>
+ <release-version>1</release-version>
+ <specification-version>1.79</specification-version>
</run-dependency>
</dependency>
<dependency>
- <code-name-base>org.netbeans.modules.parsing.lucene</code-name-base>
+ <code-name-base>org.netbeans.modules.projectuiapi</code-name-base>
<build-prerequisite/>
<compile-dependency/>
<run-dependency>
- <release-version>2</release-version>
- <specification-version>2.9</specification-version>
+ <release-version>1</release-version>
+ <specification-version>1.99</specification-version>
</run-dependency>
</dependency>
<dependency>
- <code-name-base>org.netbeans.modules.projectapi</code-name-base>
+ <code-name-base>org.netbeans.modules.refactoring.api</code-name-base>
<build-prerequisite/>
<compile-dependency/>
<run-dependency>
- <release-version>1</release-version>
- <specification-version>1.13</specification-version>
+ <specification-version>1.58</specification-version>
</run-dependency>
</dependency>
<dependency>
- <code-name-base>org.netbeans.modules.projectuiapi.base</code-name-base>
+ <code-name-base>org.netbeans.swing.plaf</code-name-base>
<build-prerequisite/>
<compile-dependency/>
<run-dependency>
- <release-version>1</release-version>
- <specification-version>1.78</specification-version>
+ <specification-version>1.53</specification-version>
</run-dependency>
</dependency>
<dependency>
@@ -222,7 +239,7 @@
<build-prerequisite/>
<compile-dependency/>
<run-dependency>
- <specification-version>6.11</specification-version>
+ <specification-version>7.79</specification-version>
</run-dependency>
</dependency>
<dependency>
@@ -230,7 +247,7 @@
<build-prerequisite/>
<compile-dependency/>
<run-dependency>
- <specification-version>7.5</specification-version>
+ <specification-version>7.53</specification-version>
</run-dependency>
</dependency>
<dependency>
@@ -238,7 +255,7 @@
<build-prerequisite/>
<compile-dependency/>
<run-dependency>
- <specification-version>9.0</specification-version>
+ <specification-version>9.21</specification-version>
</run-dependency>
</dependency>
<dependency>
@@ -246,112 +263,67 @@
<build-prerequisite/>
<compile-dependency/>
<run-dependency>
- <specification-version>7.61</specification-version>
+ <specification-version>7.80</specification-version>
</run-dependency>
</dependency>
<dependency>
- <code-name-base>org.openide.modules</code-name-base>
+ <code-name-base>org.openide.nodes</code-name-base>
<build-prerequisite/>
<compile-dependency/>
<run-dependency>
- <specification-version>7.28</specification-version>
+ <specification-version>7.56</specification-version>
</run-dependency>
</dependency>
<dependency>
- <code-name-base>org.openide.nodes</code-name-base>
+ <code-name-base>org.openide.text</code-name-base>
<build-prerequisite/>
<compile-dependency/>
<run-dependency>
- <specification-version>7.2</specification-version>
+ <specification-version>6.78</specification-version>
</run-dependency>
</dependency>
<dependency>
- <code-name-base>org.openide.text</code-name-base>
+ <code-name-base>org.openide.util</code-name-base>
<build-prerequisite/>
<compile-dependency/>
<run-dependency>
- <specification-version>6.16</specification-version>
+ <specification-version>9.18</specification-version>
</run-dependency>
</dependency>
<dependency>
- <code-name-base>org.openide.util</code-name-base>
+ <code-name-base>org.openide.util.lookup</code-name-base>
<build-prerequisite/>
<compile-dependency/>
<run-dependency>
- <specification-version>9.3</specification-version>
+ <specification-version>8.45</specification-version>
</run-dependency>
</dependency>
<dependency>
- <code-name-base>org.openide.util.lookup</code-name-base>
+ <code-name-base>org.openide.util.ui</code-name-base>
<build-prerequisite/>
<compile-dependency/>
<run-dependency>
- <specification-version>8.0</specification-version>
+ <specification-version>9.19</specification-version>
</run-dependency>
</dependency>
<dependency>
- <code-name-base>org.openide.util.ui</code-name-base>
+ <code-name-base>org.openide.windows</code-name-base>
<build-prerequisite/>
<compile-dependency/>
<run-dependency>
- <specification-version>9.3</specification-version>
+ <specification-version>6.88</specification-version>
</run-dependency>
</dependency>
</module-dependencies>
- <test-dependencies>
- <test-type>
- <name>unit</name>
- <test-dependency>
- <code-name-base>org.netbeans.core</code-name-base>
- <recursive/>
- </test-dependency>
- <test-dependency>
- <code-name-base>org.netbeans.insane</code-name-base>
- <compile-dependency/>
- </test-dependency>
- <test-dependency>
- <code-name-base>org.netbeans.libs.junit4</code-name-base>
- <compile-dependency/>
- </test-dependency>
- <test-dependency>
- <code-name-base>org.netbeans.modules.java.lexer</code-name-base>
- <compile-dependency/>
- </test-dependency>
- <test-dependency>
- <code-name-base>org.netbeans.modules.java.platform</code-name-base>
- <compile-dependency/>
- </test-dependency>
- <test-dependency>
- <code-name-base>org.netbeans.modules.java.source.base</code-name-base>
- <recursive/>
- <compile-dependency/>
- <test/>
- </test-dependency>
- <test-dependency>
- <code-name-base>org.netbeans.modules.lexer</code-name-base>
- <compile-dependency/>
- </test-dependency>
- <test-dependency>
- <code-name-base>org.netbeans.modules.nbjunit</code-name-base>
- <compile-dependency/>
- </test-dependency>
- <test-dependency>
- <code-name-base>org.netbeans.modules.parsing.api</code-name-base>
- <compile-dependency/>
- <test/>
- </test-dependency>
- <test-dependency>
- <code-name-base>org.netbeans.modules.parsing.nb</code-name-base>
- <compile-dependency/>
- </test-dependency>
- <test-dependency>
- <code-name-base>org.netbeans.modules.projectapi.nb</code-name-base>
- </test-dependency>
- </test-type>
- </test-dependencies>
- <public-packages>
- <package>org.netbeans.api.java.source.ui</package>
- </public-packages>
+ <public-packages/>
+ <class-path-extension>
+ <runtime-relative-path>ext/spring-boot-configuration-metadata-2.4.4.jar</runtime-relative-path>
+ <binary-origin>external/spring-boot-configuration-metadata-2.4.4.jar</binary-origin>
+ </class-path-extension>
+ <class-path-extension>
+ <runtime-relative-path>ext/android-json-0.0.20131108.vaadin1.jar</runtime-relative-path>
+ <binary-origin>external/android-json-0.0.20131108.vaadin1.jar</binary-origin>
+ </class-path-extension>
</data>
</configuration>
</project>
diff --git a/ide/libs.bytelist/external/binaries-list b/enterprise/micronaut/src/org/netbeans/modules/micronaut/Bundle.properties
similarity index 83%
copy from ide/libs.bytelist/external/binaries-list
copy to enterprise/micronaut/src/org/netbeans/modules/micronaut/Bundle.properties
index 0e0b70c1..b7f6a8b 100644
--- a/ide/libs.bytelist/external/binaries-list
+++ b/enterprise/micronaut/src/org/netbeans/modules/micronaut/Bundle.properties
@@ -14,5 +14,6 @@
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
-80294E59315B314D57782DC37F983AE5B29C2E4C org.jruby.extras:bytelist:1.0.15
-E2C76A19F00128BB1806207E2989139BFB45F49D org.jruby.jcodings:jcodings:1.0.18
+
+OpenIDE-Module-Name=Micronaut Support
+OpenIDE-Module-Display-Category=Java Web and EE
\ No newline at end of file
diff --git a/enterprise/micronaut/src/org/netbeans/modules/micronaut/MicronautConfigProperties.java b/enterprise/micronaut/src/org/netbeans/modules/micronaut/MicronautConfigProperties.java
new file mode 100644
index 0000000..163171c
--- /dev/null
+++ b/enterprise/micronaut/src/org/netbeans/modules/micronaut/MicronautConfigProperties.java
@@ -0,0 +1,90 @@
+/*
+ * 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;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+import org.netbeans.api.java.classpath.ClassPath;
+import org.netbeans.api.java.project.JavaProjectConstants;
+import org.netbeans.api.project.Project;
+import org.netbeans.api.project.ProjectUtils;
+import org.netbeans.api.project.SourceGroup;
+import org.openide.filesystems.FileObject;
+import org.openide.util.Exceptions;
+import org.springframework.boot.configurationmetadata.ConfigurationMetadataGroup;
+import org.springframework.boot.configurationmetadata.ConfigurationMetadataProperty;
+import org.springframework.boot.configurationmetadata.ConfigurationMetadataRepository;
+import org.springframework.boot.configurationmetadata.ConfigurationMetadataRepositoryJsonBuilder;
+
+/**
+ *
+ * @author Dusan Balek
+ */
+public final class MicronautConfigProperties {
+
+ private static final String CONFIG_METADATA_JSON = "META-INF/spring-configuration-metadata.json";
+
+ private MicronautConfigProperties() {
+ }
+
+ public static boolean hasConfigMetadata(Project project) {
+ ClassPath cp = getExecuteClasspath(project);
+ return cp != null && !cp.findAllResources(CONFIG_METADATA_JSON).isEmpty();
+ }
+
+ public static Map<String, ConfigurationMetadataProperty> getProperties(Project project) {
+ Map<String, ConfigurationMetadataProperty> props = new LinkedHashMap<>();
+ ClassPath cp = getExecuteClasspath(project);
+ if (cp != null) {
+ for (FileObject fo : cp.findAllResources(CONFIG_METADATA_JSON)) {
+ try {
+ ConfigurationMetadataRepository repository = ConfigurationMetadataRepositoryJsonBuilder.create().withJsonResource(fo.getInputStream()).build();
+ props.putAll(repository.getAllProperties());
+ } catch (Exception ex) {
+ Exceptions.printStackTrace(ex);
+ }
+ }
+ }
+ return props;
+ }
+
+ public static Map<String, ConfigurationMetadataGroup> getGroups(Project project) {
+ Map<String, ConfigurationMetadataGroup> groups = new LinkedHashMap<>();
+ ClassPath cp = getExecuteClasspath(project);
+ if (cp != null) {
+ for (FileObject fo : cp.findAllResources(CONFIG_METADATA_JSON)) {
+ try {
+ ConfigurationMetadataRepository repository = ConfigurationMetadataRepositoryJsonBuilder.create().withJsonResource(fo.getInputStream()).build();
+ groups.putAll(repository.getAllGroups());
+ } catch (Exception ex) {
+ Exceptions.printStackTrace(ex);
+ }
+ }
+ }
+ return groups;
+ }
+
+ private static ClassPath getExecuteClasspath(Project project) {
+ SourceGroup[] srcGroups = ProjectUtils.getSources(project).getSourceGroups(JavaProjectConstants.SOURCES_TYPE_JAVA);
+ if (srcGroups.length > 0) {
+ return ClassPath.getClassPath(srcGroups[0].getRootFolder(), ClassPath.EXECUTE);
+ }
+ return null;
+ }
+}
diff --git a/enterprise/micronaut/src/org/netbeans/modules/micronaut/MicronautConfigUtilities.java b/enterprise/micronaut/src/org/netbeans/modules/micronaut/MicronautConfigUtilities.java
new file mode 100644
index 0000000..b95425b
--- /dev/null
+++ b/enterprise/micronaut/src/org/netbeans/modules/micronaut/MicronautConfigUtilities.java
@@ -0,0 +1,220 @@
+/*
+ * 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;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Consumer;
+import java.util.regex.Pattern;
+import javax.swing.text.Document;
+import org.netbeans.api.editor.document.EditorDocumentUtils;
+import org.netbeans.api.editor.document.LineDocument;
+import org.netbeans.api.editor.document.LineDocumentUtils;
+import org.netbeans.api.project.FileOwnerQuery;
+import org.netbeans.api.project.Project;
+import org.netbeans.modules.csl.api.StructureItem;
+import org.netbeans.modules.csl.api.StructureScanner;
+import org.netbeans.modules.csl.core.Language;
+import org.netbeans.modules.csl.core.LanguageRegistry;
+import org.netbeans.modules.csl.spi.ParserResult;
+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.modules.parsing.spi.Parser;
+import org.openide.filesystems.FileObject;
+import org.openide.util.Exceptions;
+import org.springframework.boot.configurationmetadata.ConfigurationMetadataGroup;
+import org.springframework.boot.configurationmetadata.ConfigurationMetadataProperty;
+import org.springframework.boot.configurationmetadata.ConfigurationMetadataSource;
+
+/**
+ *
+ * @author Dusan Balek
+ */
+public class MicronautConfigUtilities {
+
+ public static ConfigurationMetadataProperty resolveProperty(Document doc, int offset, int[] span, List<ConfigurationMetadataSource> sources) {
+ LineDocument lineDocument = LineDocumentUtils.as(doc, LineDocument.class);
+ if (lineDocument != null) {
+ FileObject fo = EditorDocumentUtils.getFileObject(doc);
+ if (fo != null && "application.yml".equalsIgnoreCase(fo.getNameExt())) {
+ Project project = FileOwnerQuery.getOwner(fo);
+ if (project != null) {
+ if (MicronautConfigProperties.hasConfigMetadata(project)) {
+ try {
+ int lineStart = LineDocumentUtils.getLineStart(lineDocument, offset);
+ String text = lineDocument.getText(lineStart, offset - lineStart);
+ if (!text.contains("#")) {
+ int idx = text.indexOf(':');
+ if (idx < 0) {
+ final ConfigurationMetadataProperty[] property = new ConfigurationMetadataProperty[]{null};
+ ParserManager.parse(Collections.singleton(Source.create(lineDocument)), new UserTask() {
+ public @Override void run(ResultIterator resultIterator) throws Exception {
+ Parser.Result r = resultIterator.getParserResult();
+ if (r instanceof ParserResult) {
+ Language language = LanguageRegistry.getInstance().getLanguageByMimeType(resultIterator.getSnapshot().getMimeType());
+ if (language != null) {
+ StructureScanner scanner = language.getStructure();
+ if (scanner != null) {
+ List<? extends StructureItem> structures = scanner.scan((ParserResult) r);
+ List<StructureItem> context = getContext(structures, offset);
+ if (!context.isEmpty()) {
+ StructureItem item = context.get(context.size() - 1);
+ int start = (int) item.getPosition();
+ int end = (int) item.getPosition() + item.getName().length();
+ if (span != null && span.length == 2) {
+ span[0] = start;
+ span[1] = end;
+ }
+ if (start <= offset && offset <= end && item.getName().equals(lineDocument.getText(start, end - start))) {
+ String propertyName = getPropertyName(context);
+ for (Map.Entry<String, ConfigurationMetadataGroup> groupEntry : MicronautConfigProperties.getGroups(project).entrySet()) {
+ String groupKey = groupEntry.getKey();
+ if (Pattern.matches(groupKey.replaceAll("\\.", "\\\\.").replaceAll("\\*", "\\\\w*") + ".*", propertyName)) {
+ ConfigurationMetadataGroup group = groupEntry.getValue();
+ for (Map.Entry<String, ConfigurationMetadataProperty> propertyEntry : group.getProperties().entrySet()) {
+ String propertyKey = propertyEntry.getKey();
+ if (Pattern.matches(propertyKey.replaceAll("\\.", "\\\\.").replaceAll("\\*", "\\\\w*"), propertyName)) {
+ property[0] = propertyEntry.getValue();
+ if (sources != null) {
+ sources.addAll(group.getSources().values());
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ });
+ return property[0];
+ }
+ }
+ } catch (Exception ex) {
+ Exceptions.printStackTrace(ex);
+ }
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ public static void collectUsages(FileObject fo, String propertyName, Consumer<Usage> consumer) {
+ try {
+ ParserManager.parse(Collections.singleton(Source.create(fo)), new UserTask() {
+ public @Override void run(ResultIterator resultIterator) throws Exception {
+ Parser.Result r = resultIterator.getParserResult();
+ if (r instanceof ParserResult) {
+ Language language = LanguageRegistry.getInstance().getLanguageByMimeType(resultIterator.getSnapshot().getMimeType());
+ if (language != null) {
+ StructureScanner scanner = language.getStructure();
+ if (scanner != null) {
+ find(fo, propertyName, scanner.scan((ParserResult) r), r.getSnapshot().getText(), consumer);
+ }
+ }
+ }
+ }
+ });
+ } catch (ParseException ex) {
+ Exceptions.printStackTrace(ex);
+ }
+ }
+
+ private static void find(FileObject fo, String propertyName, List<? extends StructureItem> structures, CharSequence content, Consumer<Usage> consumer) {
+ int idx = propertyName.indexOf('.');
+ String name = idx < 0 ? propertyName : propertyName.substring(0, idx);
+ for (StructureItem structure : structures) {
+ if ("*".equals(name) || name.equals(structure.getName())) {
+ if (idx < 0) {
+ int start = (int) structure.getPosition();
+ int end = (int) structure.getEndPosition();
+ String text = content.subSequence(start, end).toString();
+ consumer.accept(new Usage(fo, start, end, text));
+ } else {
+ find(fo, propertyName.substring(idx + 1), structure.getNestedItems(), content, consumer);
+ }
+ }
+ }
+ }
+
+ private static List<StructureItem> getContext(List<? extends StructureItem> structure, int offset) {
+ List<StructureItem> context = new ArrayList<>();
+ loop: while (structure != null && !structure.isEmpty()) {
+ for (StructureItem item : structure) {
+ if (item.getPosition() <= offset && offset <= item.getEndPosition()) {
+ context.add(item);
+ structure = item.getNestedItems();
+ continue loop;
+ }
+ }
+ structure = null;
+ }
+ return context;
+ }
+
+ private static String getPropertyName(List<? extends StructureItem> context) {
+ StringBuilder sb = new StringBuilder();
+ for (StructureItem item : context) {
+ if (sb.length() > 0) {
+ sb.append('.');
+ }
+ sb.append(item.getName());
+ }
+ return sb.toString();
+ }
+
+ public static final class Usage {
+
+ private final FileObject fileObject;
+ private final int startOffset;
+ private final int endOffset;
+ private final String text;
+
+ public Usage(FileObject fileObject, int startOffset, int endOffset, String text) {
+ this.fileObject = fileObject;
+ this.startOffset = startOffset;
+ this.endOffset = endOffset;
+ this.text = text;
+ }
+
+ public FileObject getFileObject() {
+ return fileObject;
+ }
+
+ public int getStartOffset() {
+ return startOffset;
+ }
+
+ public int getEndOffset() {
+ return endOffset;
+ }
+
+ public String getText() {
+ return text;
+ }
+ }
+}
diff --git a/enterprise/micronaut/src/org/netbeans/modules/micronaut/completion/MicronautConfigCompletionCollector.java b/enterprise/micronaut/src/org/netbeans/modules/micronaut/completion/MicronautConfigCompletionCollector.java
new file mode 100644
index 0000000..e9d9316
--- /dev/null
+++ b/enterprise/micronaut/src/org/netbeans/modules/micronaut/completion/MicronautConfigCompletionCollector.java
@@ -0,0 +1,101 @@
+/*
+ * 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.function.Consumer;
+import javax.swing.text.Document;
+import org.netbeans.api.editor.document.EditorDocumentUtils;
+import org.netbeans.api.editor.mimelookup.MimeRegistration;
+import org.netbeans.api.lsp.Completion;
+import org.netbeans.api.project.FileOwnerQuery;
+import org.netbeans.api.project.Project;
+import org.netbeans.lib.editor.util.ArrayUtilities;
+import org.netbeans.modules.micronaut.MicronautConfigProperties;
+import org.netbeans.spi.lsp.CompletionCollector;
+import org.openide.filesystems.FileObject;
+import org.springframework.boot.configurationmetadata.ConfigurationMetadataProperty;
+
+/**
+ *
+ * @author Dusan Balek
+ */
+@MimeRegistration(mimeType = "text/x-yaml", service = CompletionCollector.class)
+public class MicronautConfigCompletionCollector implements CompletionCollector {
+
+ @Override
+ public boolean collectCompletions(Document doc, int offset, Completion.Context context, Consumer<Completion> consumer) {
+ FileObject fo = EditorDocumentUtils.getFileObject(doc);
+ if (fo != null && "application.yml".equalsIgnoreCase(fo.getNameExt())) {
+ Project project = FileOwnerQuery.getOwner(fo);
+ if (project != null) {
+ if (MicronautConfigProperties.hasConfigMetadata(project)) {
+ new MicronautConfigCompletionTask().query(doc, offset, project, new MicronautConfigCompletionTask.ItemFactory<Completion>() {
+ @Override
+ public Completion createTopLevelPropertyItem(String propName, int offset, int baseIndent, int indentLevelSize) {
+ StringBuilder insertText = new StringBuilder();
+ Completion.TextFormat insertTextFormat = Completion.TextFormat.PlainText;
+ if ("*".equals(propName)) {
+ insertText.append("$1:\n");
+ ArrayUtilities.appendSpaces(insertText, baseIndent + indentLevelSize);
+ insertTextFormat = Completion.TextFormat.Snippet;
+ } else {
+ insertText.append(propName).append(":\n");
+ ArrayUtilities.appendSpaces(insertText, indentLevelSize);
+ }
+ return CompletionCollector.newBuilder(propName).kind(Completion.Kind.Property).sortText(String.format("%4d%s", 10, propName))
+ .insertText(insertText.toString()).insertTextFormat(insertTextFormat).build();
+ }
+
+ @Override
+ public Completion createPropertyItem(ConfigurationMetadataProperty property, int offset, int baseIndent, int indentLevelSize, int idx) {
+ String[] parts = property.getId().substring(idx).split("\\.");
+ StringBuilder insertText = new StringBuilder();
+ int num = 1;
+ int indent = 0;
+ Completion.TextFormat insertTextFormat = Completion.TextFormat.PlainText;
+ for (int i = 0; i < parts.length; i++) {
+ String part = parts[i];
+ if ("*".equals(part)) {
+ insertText.append("$" + num++);
+ insertTextFormat = Completion.TextFormat.Snippet;
+ } else {
+ insertText.append(part);
+ }
+ if (i < parts.length - 1) {
+ insertText.append(":\n");
+ ArrayUtilities.appendSpaces(insertText, (indent = indent + indentLevelSize));
+ } else {
+ insertText.append(": ");
+ }
+ }
+ CompletionCollector.Builder builder = CompletionCollector.newBuilder(property.getId()).kind(Completion.Kind.Property)
+ .sortText(String.format("%4d%s", property.isDeprecated() ? 30 : 20, property.getId())).insertText(insertText.toString())
+ .insertTextFormat(insertTextFormat).documentation(new MicronautConfigDocumentation(property).getText());
+ if (property.isDeprecated()) {
+ builder.addTag(Completion.Tag.Deprecated);
+ }
+ return builder.build();
+ }
+ }).stream().forEach(consumer);
+ }
+ }
+ }
+ return true;
+ }
+}
diff --git a/enterprise/micronaut/src/org/netbeans/modules/micronaut/completion/MicronautConfigCompletionItem.java b/enterprise/micronaut/src/org/netbeans/modules/micronaut/completion/MicronautConfigCompletionItem.java
new file mode 100644
index 0000000..e1a5648
--- /dev/null
+++ b/enterprise/micronaut/src/org/netbeans/modules/micronaut/completion/MicronautConfigCompletionItem.java
@@ -0,0 +1,302 @@
+/*
+ * 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.awt.Color;
+import java.awt.Font;
+import java.awt.Graphics;
+import java.awt.event.InputEvent;
+import java.awt.event.KeyEvent;
+import java.util.regex.Pattern;
+import javax.swing.ImageIcon;
+import javax.swing.text.Document;
+import javax.swing.text.JTextComponent;
+import org.netbeans.api.editor.completion.Completion;
+import org.netbeans.api.editor.document.LineDocument;
+import org.netbeans.api.editor.document.LineDocumentUtils;
+import org.netbeans.lib.editor.codetemplates.api.CodeTemplateManager;
+import org.netbeans.lib.editor.util.ArrayUtilities;
+import org.netbeans.modules.editor.indent.api.IndentUtils;
+import org.netbeans.spi.editor.completion.CompletionItem;
+import org.netbeans.spi.editor.completion.CompletionTask;
+import org.netbeans.spi.editor.completion.support.CompletionUtilities;
+import org.netbeans.swing.plaf.LFCustoms;
+import org.openide.util.Exceptions;
+import org.openide.util.ImageUtilities;
+import org.openide.xml.XMLUtil;
+import org.springframework.boot.configurationmetadata.ConfigurationMetadataProperty;
+
+/**
+ *
+ * @author Dusan Balek
+ */
+public abstract class MicronautConfigCompletionItem implements CompletionItem {
+
+ public static final String PROPERTY_NAME_COLOR = getHTMLColor(64, 64, 217);
+ private static final String ICON = "org/netbeans/modules/editor/resources/completion/field_16.png"; //NOI18N
+
+ public static MicronautConfigCompletionItem createTopLevelPropertyItem(String propName, int offset, int baseIndent, int indentLevelSize) {
+ return new TopLevelItem(propName, offset, baseIndent, indentLevelSize);
+ }
+
+ public static MicronautConfigCompletionItem createPropertyItem(ConfigurationMetadataProperty property, int offset, int baseIndent, int indentLevelSize, int idx) {
+ return new PropertyItem(property, offset, baseIndent, indentLevelSize, idx);
+ }
+
+ protected final String propName;
+ protected final int offset;
+ protected final int baseIndent;
+ protected final int indentLevelSize;
+ private ImageIcon icon;
+
+ private MicronautConfigCompletionItem(String propName, int offset, int baseIndent, int indentLevelSize) {
+ this.propName = propName;
+ this.offset = offset;
+ this.baseIndent = baseIndent;
+ this.indentLevelSize = indentLevelSize;
+ }
+
+ @Override
+ public void defaultAction(JTextComponent component) {
+ if (component != null) {
+ Completion.get().hideDocumentation();
+ Completion.get().hideCompletion();
+ process(component, false);
+ }
+ }
+
+ @Override
+ public void processKeyEvent(KeyEvent evt) {
+ if (evt.getID() == KeyEvent.KEY_PRESSED && evt.getKeyCode() == KeyEvent.VK_ENTER && (evt.getModifiers() & InputEvent.CTRL_MASK) > 0) {
+ JTextComponent component = (JTextComponent)evt.getSource();
+ Completion.get().hideDocumentation();
+ Completion.get().hideCompletion();
+ process(component, true);
+ evt.consume();
+ }
+ }
+
+ @Override
+ public int getPreferredWidth(Graphics g, Font defaultFont) {
+ return CompletionUtilities.getPreferredWidth(getLeftHtmlText(), getRightHtmlText(), g, defaultFont);
+ }
+
+ @Override
+ public void render(Graphics g, Font defaultFont, Color defaultColor, Color backgroundColor, int width, int height, boolean selected) {
+ CompletionUtilities.renderHtml(getIcon(), getLeftHtmlText(), getRightHtmlText(), g, defaultFont, defaultColor, width, height, selected);
+ }
+
+ @Override
+ public CompletionTask createDocumentationTask() {
+ return null;
+ }
+
+ @Override
+ public CompletionTask createToolTipTask() {
+ return null;
+ }
+
+ @Override
+ public boolean instantSubstitution(JTextComponent component) {
+ return false;
+ }
+
+ @Override
+ public CharSequence getSortText() {
+ return propName;
+ }
+
+ @Override
+ public CharSequence getInsertPrefix() {
+ return propName;
+ }
+
+ protected String getLeftHtmlText() {
+ return null;
+ }
+
+ protected String getRightHtmlText() {
+ return null;
+ }
+
+ protected ImageIcon getIcon() {
+ if (icon == null) {
+ icon = ImageUtilities.loadImageIcon(ICON, false);
+ }
+ return icon;
+ }
+
+ protected abstract void process(JTextComponent component, boolean overwrite);
+
+ private static final class TopLevelItem extends MicronautConfigCompletionItem {
+
+ private TopLevelItem(String propName, int offset, int baseIndent, int indentLevelSize) {
+ super(propName, offset, baseIndent, indentLevelSize);
+ }
+
+ @Override
+ public int getSortPriority() {
+ return 10;
+ }
+
+ @Override
+ protected String getLeftHtmlText() {
+ return PROPERTY_NAME_COLOR + "<b>" + propName + "</b></font>";
+ }
+
+ @Override
+ protected void process(JTextComponent component, boolean overwrite) {
+ try {
+ Document doc = component.getDocument();
+ LineDocument lineDocument = LineDocumentUtils.as(doc, LineDocument.class);
+ if (lineDocument != null) {
+ int caretOffset = component.getCaretPosition();
+ int end = LineDocumentUtils.getWordEnd(lineDocument, caretOffset);
+ if (overwrite && LineDocumentUtils.getWordStart(lineDocument, end) == offset) {
+ if (doc.getText(end, 1).endsWith(":")) {
+ end++;
+ }
+ doc.remove(offset, Math.max(caretOffset, end) - offset);
+ } else if (offset < caretOffset) {
+ doc.remove(offset, caretOffset - offset);
+ }
+ int lineStart = LineDocumentUtils.getLineStart(lineDocument, caretOffset);
+ int lineIndent = IndentUtils.lineIndent(doc, lineStart);
+ StringBuilder sb = new StringBuilder();
+ ArrayUtilities.appendSpaces(sb, baseIndent - lineIndent);
+ sb.append("*".equals(propName) ? "${PAR#1 default=\"\"}" : propName).append(":\n");
+ ArrayUtilities.appendSpaces(sb, baseIndent + indentLevelSize);
+ sb.append("${cursor completionInvoke}");
+ CodeTemplateManager.get(doc).createTemporary(sb.toString()).insert(component);
+ }
+ } catch (Exception ex) {
+ Exceptions.printStackTrace(ex);
+ }
+ }
+ }
+
+ private static final class PropertyItem extends MicronautConfigCompletionItem {
+
+ private static final Pattern FQN = Pattern.compile("(\\w+\\.)+(\\w+)");
+ private final ConfigurationMetadataProperty property;
+ private final int idx;
+ private String left;
+ private String right;
+
+ private PropertyItem(ConfigurationMetadataProperty property, int offset, int baseIndent, int indentLevelSize, int idx) {
+ super(property.getId(), offset, baseIndent, indentLevelSize);
+ this.property = property;
+ this.idx = idx;
+ }
+
+ @Override
+ public CompletionTask createDocumentationTask() {
+ return MicronautConfigCompletionProvider.createDocTask(property);
+ }
+
+ @Override
+ public int getSortPriority() {
+ return property.isDeprecated() ? 30 : 20;
+ }
+
+ @Override
+ protected String getLeftHtmlText() {
+ if (left == null) {
+ if (property.isDeprecated()) {
+ left = PROPERTY_NAME_COLOR + "<s>" + propName + "</s></font>";
+ } else {
+ left = PROPERTY_NAME_COLOR + propName + "</font>";
+ }
+ }
+ return left;
+ }
+
+ @Override
+ protected String getRightHtmlText() {
+ if (right == null) {
+ String type = property.getType();
+ right = type != null ? escape(FQN.matcher(type).replaceAll("$2")) : "";
+ }
+ return right;
+ }
+
+ @Override
+ protected void process(JTextComponent component, boolean overwrite) {
+ try {
+ Document doc = component.getDocument();
+ LineDocument lineDocument = LineDocumentUtils.as(doc, LineDocument.class);
+ if (lineDocument != null) {
+ int caretOffset = component.getCaretPosition();
+ int end = LineDocumentUtils.getWordEnd(lineDocument, caretOffset);
+ if (overwrite && LineDocumentUtils.getWordStart(lineDocument, end) == offset) {
+ if (doc.getText(end, 1).endsWith(":")) {
+ end++;
+ }
+ doc.remove(offset, Math.max(caretOffset, end) - offset);
+ } else if (offset < caretOffset) {
+ doc.remove(offset, caretOffset - offset);
+ }
+ int lineStart = LineDocumentUtils.getLineStart(lineDocument, caretOffset);
+ int lineIndent = IndentUtils.lineIndent(doc, lineStart);
+ StringBuilder sb = new StringBuilder();
+ ArrayUtilities.appendSpaces(sb, baseIndent - lineIndent);
+ String name = propName.substring(idx);
+ int indent = baseIndent;
+ final String[] parts = name.split("\\.");
+ int num = 1;
+ for (int i = 0; i < parts.length; i++) {
+ String part = parts[i];
+ if ("*".equals(part)) {
+ sb.append("${PAR#" + num++ + " default=\"\"}");
+ } else {
+ sb.append(part);
+ }
+ if (i < parts.length - 1) {
+ sb.append(":\n");
+ ArrayUtilities.appendSpaces(sb, (indent = indent + indentLevelSize));
+ } else {
+ sb.append(": ${cursor}");
+ }
+ }
+ CodeTemplateManager.get(doc).createTemporary(sb.toString()).insert(component);
+ }
+ } catch (Exception ex) {
+ Exceptions.printStackTrace(ex);
+ }
+ }
+ }
+
+ private static String getHTMLColor(int r, int g, int b) {
+ Color c = LFCustoms.shiftColor(new Color(r, g, b));
+ return "<font color=#" //NOI18N
+ + LFCustoms.getHexString(c.getRed())
+ + LFCustoms.getHexString(c.getGreen())
+ + LFCustoms.getHexString(c.getBlue())
+ + ">"; //NOI18N
+ }
+
+ private static String escape(String s) {
+ if (s != null) {
+ try {
+ return XMLUtil.toAttributeValue(s);
+ } catch (Exception ex) {}
+ }
+ return s;
+ }
+}
diff --git a/enterprise/micronaut/src/org/netbeans/modules/micronaut/completion/MicronautConfigCompletionProvider.java b/enterprise/micronaut/src/org/netbeans/modules/micronaut/completion/MicronautConfigCompletionProvider.java
new file mode 100644
index 0000000..788e0da
--- /dev/null
+++ b/enterprise/micronaut/src/org/netbeans/modules/micronaut/completion/MicronautConfigCompletionProvider.java
@@ -0,0 +1,117 @@
+/*
+ * 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 javax.swing.text.Document;
+import javax.swing.text.JTextComponent;
+import org.netbeans.api.editor.document.EditorDocumentUtils;
+import org.netbeans.api.editor.mimelookup.MimeRegistration;
+import org.netbeans.api.project.FileOwnerQuery;
+import org.netbeans.api.project.Project;
+import org.netbeans.modules.micronaut.MicronautConfigProperties;
+import org.netbeans.modules.micronaut.MicronautConfigUtilities;
+import org.netbeans.spi.editor.completion.CompletionProvider;
+import org.netbeans.spi.editor.completion.CompletionResultSet;
+import org.netbeans.spi.editor.completion.CompletionTask;
+import org.netbeans.spi.editor.completion.support.AsyncCompletionQuery;
+import org.netbeans.spi.editor.completion.support.AsyncCompletionTask;
+import org.openide.filesystems.FileObject;
+import org.springframework.boot.configurationmetadata.ConfigurationMetadataProperty;
+
+/**
+ *
+ * @author Dusan Balek
+ */
+@MimeRegistration(mimeType = "text/x-yaml", service = CompletionProvider.class)
+public class MicronautConfigCompletionProvider implements CompletionProvider {
+
+ @Override
+ public CompletionTask createTask(int queryType, JTextComponent component) {
+ FileObject fo = EditorDocumentUtils.getFileObject(component.getDocument());
+ if (fo != null && "application.yml".equalsIgnoreCase(fo.getNameExt())) {
+ Project project = FileOwnerQuery.getOwner(fo);
+ if (project != null) {
+ if (MicronautConfigProperties.hasConfigMetadata(project)) {
+ switch (queryType) {
+ case COMPLETION_ALL_QUERY_TYPE:
+ case COMPLETION_QUERY_TYPE:
+ return new AsyncCompletionTask(new MicronautConfigCompletionQuery(project), component);
+ case DOCUMENTATION_QUERY_TYPE:
+ return new AsyncCompletionTask(new MicronautConfigDocumentationQuery(null), component);
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public int getAutoQueryTypes(JTextComponent component, String typedText) {
+ return 0;
+ }
+
+ static CompletionTask createDocTask(ConfigurationMetadataProperty element) {
+ return new AsyncCompletionTask(new MicronautConfigDocumentationQuery(element));
+ }
+
+ private static class MicronautConfigCompletionQuery extends AsyncCompletionQuery {
+
+ private final Project project;
+
+ public MicronautConfigCompletionQuery(Project project) {
+ this.project = project;
+ }
+
+ @Override
+ protected void query(CompletionResultSet resultSet, Document doc, int caretOffset) {
+ resultSet.addAllItems(new MicronautConfigCompletionTask().query(doc, caretOffset, project, new MicronautConfigCompletionTask.ItemFactory<MicronautConfigCompletionItem>() {
+ @Override
+ public MicronautConfigCompletionItem createPropertyItem(ConfigurationMetadataProperty property, int offset, int baseIndent, int indentLevelSize, int idx) {
+ resultSet.setAnchorOffset(offset);
+ return MicronautConfigCompletionItem.createPropertyItem(property, offset, baseIndent, indentLevelSize, idx);
+ }
+
+ @Override
+ public MicronautConfigCompletionItem createTopLevelPropertyItem(String propName, int offset, int baseIndent, int indentLevelSize) {
+ resultSet.setAnchorOffset(offset);
+ return MicronautConfigCompletionItem.createTopLevelPropertyItem(propName, offset, baseIndent, indentLevelSize);
+ }
+ }));
+ resultSet.finish();
+ }
+ }
+
+ private static class MicronautConfigDocumentationQuery extends AsyncCompletionQuery {
+
+ private ConfigurationMetadataProperty element;
+
+ private MicronautConfigDocumentationQuery(ConfigurationMetadataProperty element) {
+ this.element = element;
+ }
+
+ @Override
+ protected void query(CompletionResultSet resultSet, Document doc, int caretOffset) {
+ if (element == null) {
+ element = MicronautConfigUtilities.resolveProperty(doc, caretOffset, null, null);
+ }
+ resultSet.setDocumentation(new MicronautConfigDocumentation(element));
+ resultSet.finish();
+ }
+ }
+}
diff --git a/enterprise/micronaut/src/org/netbeans/modules/micronaut/completion/MicronautConfigCompletionTask.java b/enterprise/micronaut/src/org/netbeans/modules/micronaut/completion/MicronautConfigCompletionTask.java
new file mode 100644
index 0000000..4d6f914
--- /dev/null
+++ b/enterprise/micronaut/src/org/netbeans/modules/micronaut/completion/MicronautConfigCompletionTask.java
@@ -0,0 +1,207 @@
+/*
+ * 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.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import javax.swing.text.BadLocationException;
+import javax.swing.text.Document;
+import org.netbeans.api.editor.document.LineDocument;
+import org.netbeans.api.editor.document.LineDocumentUtils;
+import org.netbeans.api.project.Project;
+import org.netbeans.modules.csl.api.StructureItem;
+import org.netbeans.modules.csl.api.StructureScanner;
+import org.netbeans.modules.csl.core.Language;
+import org.netbeans.modules.csl.core.LanguageRegistry;
+import org.netbeans.modules.csl.spi.ParserResult;
+import org.netbeans.modules.editor.indent.api.IndentUtils;
+import org.netbeans.modules.micronaut.MicronautConfigProperties;
+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.Parser;
+import org.openide.util.Exceptions;
+import org.springframework.boot.configurationmetadata.ConfigurationMetadataProperty;
+
+/**
+ *
+ * @author Dusan Balek
+ */
+public class MicronautConfigCompletionTask {
+
+ public static interface ItemFactory<T> {
+ T createTopLevelPropertyItem(String propName, int offset, int baseIndent, int indentLevelSize);
+ T createPropertyItem(ConfigurationMetadataProperty property, int offset, int baseIndent, int indentLevelSize, int idx);
+ }
+
+ public <T> List<T> query(Document doc, int caretOffset, Project project, ItemFactory<T> factory) {
+ List<T> items = new ArrayList<>();
+ LineDocument lineDocument = LineDocumentUtils.as(doc, LineDocument.class);
+ if (lineDocument != null) {
+ int lineStart = LineDocumentUtils.getLineStart(lineDocument, caretOffset);
+ try {
+ String text = lineDocument.getText(lineStart, caretOffset - lineStart);
+ if (!text.contains("#")) {
+ int idx = text.indexOf(':');
+ if (idx < 0) {
+ final int lineIndent = Math.min(IndentUtils.lineIndent(lineDocument, lineStart), caretOffset - lineStart);
+ final int wordStart = LineDocumentUtils.getPreviousWhitespace(lineDocument, caretOffset) + 1;
+ final String prefix = wordStart < caretOffset ? text.substring(wordStart - lineStart) : null;
+ final int anchorOffset = wordStart < caretOffset ? wordStart : caretOffset;
+ ParserManager.parse(Collections.singleton(Source.create(lineDocument)), new UserTask() {
+ public @Override void run(ResultIterator resultIterator) throws Exception {
+ Parser.Result r = resultIterator.getParserResult();
+ if (r instanceof ParserResult) {
+ Language language = LanguageRegistry.getInstance().getLanguageByMimeType(resultIterator.getSnapshot().getMimeType());
+ if (language != null) {
+ StructureScanner scanner = language.getStructure();
+ if (scanner != null) {
+ List<? extends StructureItem> structures = scanner.scan((ParserResult) r);
+ int indentLevelSize = getIndentLevelSize(lineDocument, structures);
+ List<? extends StructureItem> context = getContext(structures, wordStart);
+ String filter = "";
+ StructureItem currentItem = null;
+ for (StructureItem item : context) {
+ int itemLineStart = LineDocumentUtils.getLineStart(lineDocument, (int) item.getPosition());
+ int itemLineIndent = IndentUtils.lineIndent(lineDocument, itemLineStart);
+ if (itemLineIndent < lineIndent) {
+ filter += item.getName() + '.';
+ currentItem = item;
+ }
+ }
+ int filterLength = filter.length();
+ if (prefix != null) {
+ filter += prefix;
+ }
+ int currentItemLineIndent = currentItem != null ? IndentUtils.lineIndent(lineDocument, LineDocumentUtils.getLineStart(lineDocument, (int) currentItem.getPosition())) : -indentLevelSize;
+ Map<String, ConfigurationMetadataProperty> properties = MicronautConfigProperties.getProperties(project);
+ Set<String> topLevels = new HashSet<>();
+ for (Map.Entry<String, ConfigurationMetadataProperty> entry : properties.entrySet()) {
+ String propName = entry.getKey();
+ int idx = match(propName, filter);
+ if (idx >= 0) {
+ int dotIdx = propName.indexOf('.', idx);
+ String simpleName = dotIdx < 0 ? propName.substring(idx) : propName.substring(idx, dotIdx);
+ if (filter(currentItem != null ? currentItem.getNestedItems() : structures, simpleName)) {
+ items.add(factory.createPropertyItem(entry.getValue(), anchorOffset, currentItemLineIndent + indentLevelSize, indentLevelSize, idx));
+ if (dotIdx > 0) {
+ topLevels.add(simpleName);
+ }
+ }
+ }
+ }
+ for (String topLevel : topLevels) {
+ items.add(factory.createTopLevelPropertyItem(topLevel, anchorOffset, currentItemLineIndent + indentLevelSize, indentLevelSize));
+ }
+ }
+ }
+ }
+ }
+ });
+ }
+ }
+ } catch (Exception ex) {
+ Exceptions.printStackTrace(ex);
+ }
+ }
+ return items;
+ }
+
+ private int match(String propName, String filter) {
+ int len = 0;
+ String[] parts = propName.split("\\*");
+ for (int i = 0; i < parts.length; i++) {
+ String part = parts[i];
+ if (filter.length() <= part.length()) {
+ if (part.startsWith(filter)) {
+ int idx = filter.lastIndexOf('.');
+ return idx < 0 ? len : len + idx + 1;
+ } else {
+ return -1;
+ }
+ }
+ if (i < parts.length - 1) {
+ if (!part.equals(filter.substring(0, part.length()))) {
+ return -1;
+ }
+ len += part.length();
+ int idx = filter.indexOf('.', part.length());
+ if (idx < 0) {
+ return -1;
+ }
+ len += 1;
+ filter = filter.substring(idx);
+ } else {
+ return filter.startsWith(part) ? len : -1;
+ }
+ }
+ return -1;
+ }
+
+ private boolean filter(List<? extends StructureItem> structures, String name) {
+ for (StructureItem item : structures) {
+ if (name.equals(item.getName())) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private List<StructureItem> getContext(List<? extends StructureItem> structure, int offset) {
+ List<StructureItem> items = new ArrayList<>();
+ while (structure != null && !structure.isEmpty()) {
+ StructureItem currentItem = null;
+ for (StructureItem item : structure) {
+ if (item.getPosition() < offset) {
+ currentItem = item;
+ }
+ }
+ if (currentItem != null) {
+ items.add(currentItem);
+ structure = currentItem.getNestedItems();
+ } else {
+ structure = null;
+ }
+ }
+ return items;
+ }
+
+ private int getIndentLevelSize(LineDocument lineDocument, List<? extends StructureItem> structures) {
+ int indentLevel = IndentUtils.indentLevelSize(lineDocument);
+ try {
+ for (StructureItem structure : structures) {
+ int baseStart = LineDocumentUtils.getLineStart(lineDocument, (int) structure.getPosition());
+ int baseIndent = IndentUtils.lineIndent(lineDocument, baseStart);
+ for (StructureItem nestedItem : structure.getNestedItems()) {
+ int lineStart = LineDocumentUtils.getLineStart(lineDocument, (int) nestedItem.getPosition());
+ int lineIndent = IndentUtils.lineIndent(lineDocument, lineStart) - baseIndent;
+ if (lineIndent > 0 && lineIndent < indentLevel) {
+ indentLevel = lineIndent;
+ }
+ }
+ }
+ } catch (BadLocationException ble) {}
+ return indentLevel;
+ }
+}
diff --git a/enterprise/micronaut/src/org/netbeans/modules/micronaut/completion/MicronautConfigDocumentation.java b/enterprise/micronaut/src/org/netbeans/modules/micronaut/completion/MicronautConfigDocumentation.java
new file mode 100644
index 0000000..d003cce
--- /dev/null
+++ b/enterprise/micronaut/src/org/netbeans/modules/micronaut/completion/MicronautConfigDocumentation.java
@@ -0,0 +1,104 @@
+/*
+ * 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.net.URL;
+import javax.swing.Action;
+import org.netbeans.spi.editor.completion.CompletionDocumentation;
+import org.springframework.boot.configurationmetadata.ConfigurationMetadataProperty;
+import org.springframework.boot.configurationmetadata.Deprecation;
+
+/**
+ *
+ * @author Dusan Balek
+ */
+public class MicronautConfigDocumentation implements CompletionDocumentation {
+
+ private final ConfigurationMetadataProperty element;
+
+ MicronautConfigDocumentation(ConfigurationMetadataProperty element) {
+ this.element = element;
+ }
+
+ @Override
+ public String getText() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("<b>").append(makeNameLineBreakable(element.getId())).append("</b>");
+ String type = element.getType();
+ if (type != null) {
+ sb.append("<pre>").append(type).append("</pre>");
+ }
+ String description = element.getDescription();
+ String params = null;
+ if (description != null) {
+ int idx = description.indexOf("@param");
+ if (idx >= 0) {
+ params = description.substring(idx + 6).trim();
+ description = description.substring(0, idx).trim();
+ }
+ if (!description.isEmpty()) {
+ sb.append("<p>").append(description).append("</p>");
+ }
+ }
+ Deprecation deprecation = element.getDeprecation();
+ if (deprecation != null) {
+ sb.append("<p><b>Deprecated");
+ String reason = deprecation.getReason();
+ if (reason != null) {
+ sb.append(":</b> <i>").append(reason).append("</i>");
+ } else {
+ sb.append("</b>");
+ }
+ String replacement = deprecation.getReplacement();
+ if (replacement != null) {
+ sb.append("<br/>Replaced by: <code>").append(replacement).append("</code>");
+ }
+ sb.append("</p>");
+ }
+ if (params != null) {
+ sb.append("<p><b>Parameters:</b><blockquote>");
+ int idx = params.indexOf(' ');
+ if (idx < 0) {
+ sb.append(params).append("</p>");
+ } else {
+ sb.append("<code>").append(params.substring(0, idx)).append("</code>").append(params.substring(idx)).append("</blockquote></p>");
+ }
+ }
+ return sb.toString();
+ }
+
+ @Override
+ public URL getURL() {
+ return null;
+ }
+
+ @Override
+ public CompletionDocumentation resolveLink(String link) {
+ return null;
+ }
+
+ @Override
+ public Action getGotoSourceAction() {
+ return null;
+ }
+
+ private String makeNameLineBreakable(String name) {
+ return name.replace(".", /* ZERO WIDTH SPACE */".​");
+ }
+}
diff --git a/enterprise/micronaut/src/org/netbeans/modules/micronaut/completion/MicronautJavaConfigPropertiesCompletion.java b/enterprise/micronaut/src/org/netbeans/modules/micronaut/completion/MicronautJavaConfigPropertiesCompletion.java
new file mode 100644
index 0000000..085027c
--- /dev/null
+++ b/enterprise/micronaut/src/org/netbeans/modules/micronaut/completion/MicronautJavaConfigPropertiesCompletion.java
@@ -0,0 +1,140 @@
+/*
+ * 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 com.sun.source.util.TreePath;
+import com.sun.source.util.Trees;
+import java.lang.ref.Reference;
+import java.lang.ref.WeakReference;
+import java.net.MalformedURLException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import javax.annotation.processing.Completion;
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.annotation.processing.Processor;
+import javax.annotation.processing.RoundEnvironment;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.Name;
+import javax.lang.model.element.TypeElement;
+import org.netbeans.api.project.FileOwnerQuery;
+import org.netbeans.api.project.Project;
+import org.netbeans.modules.micronaut.MicronautConfigProperties;
+import org.openide.filesystems.FileObject;
+import org.openide.filesystems.URLMapper;
+import org.openide.util.Exceptions;
+import org.springframework.boot.configurationmetadata.ConfigurationMetadataProperty;
+
+/**
+ *
+ * @author Dusan Balek
+ */
+public class MicronautJavaConfigPropertiesCompletion implements Processor {
+
+ private static final Set<String> supportedAnnotationTypes = new HashSet<String>(Arrays.asList("io.micronaut.context.annotation.Property", "io.micronaut.context.annotation.Value"));
+ private Reference<ProcessingEnvironment> processingEnv;
+
+ @Override
+ public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
+ return false;
+ }
+
+ @Override
+ public Iterable<? extends Completion> getCompletions(Element element, AnnotationMirror annotation, ExecutableElement member, String userText) {
+ if (member != null && annotation != null) {
+ ProcessingEnvironment env = this.processingEnv.get();
+ if (env != null) {
+ Name annotationTypeSimpleName = annotation.getAnnotationType().asElement().getSimpleName();
+ String prefix = null;
+ String postfix = null;
+ if ("Property".contentEquals(annotationTypeSimpleName)) {
+ if ("name".contentEquals(member.getSimpleName())) {
+ prefix = "\"";
+ postfix = prefix;
+ }
+ } else if ("Value".contentEquals(annotationTypeSimpleName)) {
+ prefix = "\"${";
+ postfix = "}\"";
+ }
+ if (prefix != null && postfix != null) {
+ Trees trees = Trees.instance(env);
+ TreePath path = trees.getPath(element);
+ if (path != null) {
+ FileObject fo;
+ try {
+ fo = URLMapper.findFileObject(path.getCompilationUnit().getSourceFile().toUri().toURL());
+ } catch (MalformedURLException ex) {
+ Exceptions.printStackTrace(ex);
+ return Collections.emptyList();
+ }
+ Project project = fo != null ? FileOwnerQuery.getOwner(fo) : null;
+ if (project != null) {
+ Map<String, ConfigurationMetadataProperty> properties = MicronautConfigProperties.getProperties(project);
+ List<Completion> ret = new ArrayList<>(properties.size());
+ String format = prefix + "%s" + postfix;
+ for (ConfigurationMetadataProperty property : properties.values()) {
+ if (!property.getId().contains("*")) {
+ ret.add(new Completion() {
+ @Override
+ public String getValue() {
+ return String.format(format, property.getId());
+ }
+ @Override
+ public String getMessage() {
+ return new MicronautConfigDocumentation(property).getText();
+ }
+ });
+ }
+ }
+ return ret;
+ }
+ }
+ }
+ }
+ }
+ return Collections.emptyList();
+ }
+
+ @Override
+ public Set<String> getSupportedOptions() {
+ return Collections.emptySet();
+ }
+
+ @Override
+ public Set<String> getSupportedAnnotationTypes() {
+ return supportedAnnotationTypes;
+ }
+
+ @Override
+ public SourceVersion getSupportedSourceVersion() {
+ return SourceVersion.latest();
+ }
+
+ @Override
+ public void init(ProcessingEnvironment processingEnv) {
+ this.processingEnv = new WeakReference<ProcessingEnvironment>(processingEnv);
+ }
+}
diff --git a/enterprise/micronaut/src/org/netbeans/modules/micronaut/hyperlink/MicronautConfigHyperlinkProvider.java b/enterprise/micronaut/src/org/netbeans/modules/micronaut/hyperlink/MicronautConfigHyperlinkProvider.java
new file mode 100644
index 0000000..bd77d7a
--- /dev/null
+++ b/enterprise/micronaut/src/org/netbeans/modules/micronaut/hyperlink/MicronautConfigHyperlinkProvider.java
@@ -0,0 +1,152 @@
+/*
+ * 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.hyperlink;
+
+import java.awt.Toolkit;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.EnumSet;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.atomic.AtomicBoolean;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.util.ElementFilter;
+import javax.swing.text.Document;
+import org.netbeans.api.editor.mimelookup.MimeRegistration;
+import org.netbeans.api.java.source.ClasspathInfo;
+import org.netbeans.api.java.source.ElementHandle;
+import org.netbeans.api.java.source.JavaSource;
+import org.netbeans.api.java.source.ui.ElementOpen;
+import org.netbeans.api.lsp.HyperlinkLocation;
+import org.netbeans.api.progress.BaseProgressUtils;
+import org.netbeans.lib.editor.hyperlink.spi.HyperlinkProviderExt;
+import org.netbeans.lib.editor.hyperlink.spi.HyperlinkType;
+import org.netbeans.modules.micronaut.MicronautConfigUtilities;
+import org.netbeans.spi.lsp.HyperlinkLocationProvider;
+import org.openide.util.NbBundle;
+import org.springframework.boot.configurationmetadata.ConfigurationMetadataProperty;
+import org.springframework.boot.configurationmetadata.ConfigurationMetadataSource;
+
+/**
+ *
+ * @author Dusan Balek
+ */
+@MimeRegistration(mimeType = "text/x-yaml", service = HyperlinkProviderExt.class, position = 1250)
+public class MicronautConfigHyperlinkProvider implements HyperlinkProviderExt {
+
+ @Override
+ public Set<HyperlinkType> getSupportedHyperlinkTypes() {
+ return EnumSet.of(HyperlinkType.GO_TO_DECLARATION);
+ }
+
+ @Override
+ public boolean isHyperlinkPoint(Document doc, int offset, HyperlinkType type) {
+ return getHyperlinkSpan(doc, offset, type) != null;
+ }
+
+ @Override
+ public int[] getHyperlinkSpan(Document doc, int offset, HyperlinkType type) {
+ int[] span = new int[2];
+ ConfigurationMetadataProperty property = MicronautConfigUtilities.resolveProperty(doc, offset, span, null);
+ return property != null ? span : null;
+ }
+
+ @Override
+ @NbBundle.Messages("LBL_GoToDeclaration=Go to Declaration")
+ public void performClickAction(Document doc, int offset, HyperlinkType type) {
+ AtomicBoolean cancel = new AtomicBoolean();
+ BaseProgressUtils.runOffEventDispatchThread(() -> {
+ List<ConfigurationMetadataSource> sources = new ArrayList<>();
+ ConfigurationMetadataProperty property = MicronautConfigUtilities.resolveProperty(doc, offset, null, sources);
+ if (property != null && !sources.isEmpty()) {
+ ClasspathInfo cpInfo = ClasspathInfo.create(doc);
+ ElementHandle handle = getElementHandle(cpInfo, sources.get(0).getType(), property.getName(), cancel);
+ if (handle == null || !ElementOpen.open(cpInfo, handle)) {
+ Toolkit.getDefaultToolkit().beep();
+ }
+ }
+ }, Bundle.LBL_GoToDeclaration(), cancel, false);
+ }
+
+ @Override
+ public String getTooltipText(Document doc, int offset, HyperlinkType type) {
+ ConfigurationMetadataProperty property = MicronautConfigUtilities.resolveProperty(doc, offset, null, null);
+ if (property != null) {
+ StringBuilder sb = new StringBuilder("<html><body>");
+ sb.append("<b>").append(property.getId().replace(".", /* ZERO WIDTH SPACE */".​")).append("</b>");
+ String propertyType = property.getType();
+ if (propertyType != null) {
+ sb.append("<pre>").append(propertyType).append("</pre>");
+ }
+ return sb.toString();
+ }
+ return null;
+ }
+
+ private static ElementHandle getElementHandle(ClasspathInfo cpInfo, String typeName, String propertyName, AtomicBoolean cancel) {
+ ElementHandle[] handle = new ElementHandle[1];
+ if (cpInfo != null) {
+ try {
+ JavaSource.create(cpInfo).runUserActionTask(controller -> {
+ if (cancel != null && cancel.get()) {
+ return;
+ }
+ handle[0] = ElementHandle.createTypeElementHandle(ElementKind.CLASS, typeName);
+ TypeElement te = (TypeElement) handle[0].resolve(controller);
+ if (te != null) {
+ String name = "set" + propertyName.replaceAll("-", "");
+ for (ExecutableElement executableElement : ElementFilter.methodsIn(te.getEnclosedElements())) {
+ if (name.equalsIgnoreCase(executableElement.getSimpleName().toString())) {
+ handle[0] = ElementHandle.create(executableElement);
+ break;
+ }
+ }
+ }
+ }, true);
+ } catch (IOException ex) {}
+ }
+ return handle[0];
+ }
+
+ @MimeRegistration(mimeType = "text/x-yaml", service = HyperlinkLocationProvider.class)
+ public static class LocationProvider implements HyperlinkLocationProvider {
+
+ @Override
+ public CompletableFuture<HyperlinkLocation> getHyperlinkLocation(Document doc, int offset) {
+ final AtomicBoolean cancel = new AtomicBoolean();
+ List<ConfigurationMetadataSource> sources = new ArrayList<>();
+ ConfigurationMetadataProperty property = MicronautConfigUtilities.resolveProperty(doc, offset, null, sources);
+ if (property != null && !sources.isEmpty()) {
+ ClasspathInfo cpInfo = ClasspathInfo.create(doc);
+ String typeName = sources.get(0).getType();
+ ElementHandle handle = getElementHandle(cpInfo, typeName, property.getName(), cancel);
+ if (handle != null) {
+ CompletableFuture<ElementOpen.Location> future = ElementOpen.getLocation(cpInfo, handle, typeName.replace('.', '/') + ".class");
+ return future.thenApply(location -> {
+ return location != null ? HyperlinkLocationProvider.createHyperlinkLocation(location.getFileObject(), location.getStartOffset(), location.getEndOffset()) : null;
+ });
+ }
+ }
+ return CompletableFuture.completedFuture(null);
+ }
+ }
+}
diff --git a/enterprise/micronaut/src/org/netbeans/modules/micronaut/hyperlink/MicronautJavaHyperlinkProvider.java b/enterprise/micronaut/src/org/netbeans/modules/micronaut/hyperlink/MicronautJavaHyperlinkProvider.java
new file mode 100644
index 0000000..d79dc84
--- /dev/null
+++ b/enterprise/micronaut/src/org/netbeans/modules/micronaut/hyperlink/MicronautJavaHyperlinkProvider.java
@@ -0,0 +1,205 @@
+/*
+ * 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.hyperlink;
+
+import com.sun.source.tree.AnnotationTree;
+import com.sun.source.tree.AssignmentTree;
+import com.sun.source.tree.ExpressionTree;
+import com.sun.source.tree.LiteralTree;
+import com.sun.source.tree.Tree;
+import com.sun.source.util.SourcePositions;
+import com.sun.source.util.TreePath;
+import java.awt.Toolkit;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.EnumSet;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.Name;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeKind;
+import javax.swing.text.Document;
+import org.netbeans.api.editor.document.EditorDocumentUtils;
+import org.netbeans.api.editor.mimelookup.MimeRegistration;
+import org.netbeans.api.java.project.JavaProjectConstants;
+import org.netbeans.api.java.source.JavaSource;
+import org.netbeans.api.java.source.TreeUtilities;
+import org.netbeans.api.lsp.HyperlinkLocation;
+import org.netbeans.api.project.FileOwnerQuery;
+import org.netbeans.api.project.Project;
+import org.netbeans.api.project.ProjectUtils;
+import org.netbeans.api.project.SourceGroup;
+import org.netbeans.lib.editor.hyperlink.spi.HyperlinkProviderExt;
+import org.netbeans.lib.editor.hyperlink.spi.HyperlinkType;
+import org.netbeans.modules.csl.api.UiUtils;
+import org.netbeans.modules.micronaut.MicronautConfigProperties;
+import org.netbeans.modules.micronaut.MicronautConfigUtilities;
+import org.netbeans.spi.lsp.HyperlinkLocationProvider;
+import org.openide.filesystems.FileObject;
+import org.openide.util.Exceptions;
+import org.springframework.boot.configurationmetadata.ConfigurationMetadataProperty;
+
+/**
+ *
+ * @author Dusan Balek
+ */
+@MimeRegistration(mimeType = "text/x-java", service = HyperlinkProviderExt.class, position = 1250)
+public class MicronautJavaHyperlinkProvider implements HyperlinkProviderExt {
+
+ private static final Pattern INJECT = Pattern.compile("\\$\\{(\\S+)(:\\S*)?}");
+
+ @Override
+ public Set<HyperlinkType> getSupportedHyperlinkTypes() {
+ return EnumSet.of(HyperlinkType.GO_TO_DECLARATION);
+ }
+
+ @Override
+ public boolean isHyperlinkPoint(Document doc, int offset, HyperlinkType type) {
+ return getHyperlinkSpan(doc, offset, type) != null;
+ }
+
+ @Override
+ public int[] getHyperlinkSpan(Document doc, int offset, HyperlinkType type) {
+ int[] span = new int[2];
+ MicronautConfigUtilities.Usage usage = resolve(doc, offset, span);
+ return usage != null ? span : null;
+ }
+
+ @Override
+ public void performClickAction(Document doc, int offset, HyperlinkType type) {
+ MicronautConfigUtilities.Usage usage = resolve(doc, offset, null);
+ if (usage == null || !UiUtils.open(usage.getFileObject(), usage.getStartOffset())) {
+ Toolkit.getDefaultToolkit().beep();
+ }
+ }
+
+ @Override
+ public String getTooltipText(Document doc, int offset, HyperlinkType type) {
+ MicronautConfigUtilities.Usage usage = resolve(doc, offset, null);
+ return usage != null ? usage.getText() : null;
+ }
+
+ private static MicronautConfigUtilities.Usage resolve(Document doc, int offset, int[] span) {
+ FileObject fo = EditorDocumentUtils.getFileObject(doc);
+ if (fo != null) {
+ Project project = FileOwnerQuery.getOwner(fo);
+ if (project != null) {
+ if (MicronautConfigProperties.hasConfigMetadata(project)) {
+ SourceGroup[] sourceGroups = ProjectUtils.getSources(project).getSourceGroups(JavaProjectConstants.SOURCES_TYPE_RESOURCES);
+ for (SourceGroup sourceGroup : sourceGroups) {
+ FileObject rootFolder = sourceGroup.getRootFolder();
+ FileObject cfgFo = rootFolder.getFileObject("application.yml");
+ if (cfgFo != null) {
+ JavaSource source = JavaSource.forDocument(doc);
+ if (source != null) {
+ try {
+ String propertyName = getPropertyName(doc, offset, span);
+ ConfigurationMetadataProperty property = MicronautConfigProperties.getProperties(project).get(propertyName);
+ if (property != null) {
+ List<MicronautConfigUtilities.Usage> usages = new ArrayList<>();
+ MicronautConfigUtilities.collectUsages(cfgFo, propertyName, usage -> {
+ usages.add(usage);
+ });
+ if (!usages.isEmpty()) {
+ return usages.get(0);
+ }
+ }
+ } catch (Exception ex) {
+ Exceptions.printStackTrace(ex);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ private static String getPropertyName(Document doc, int offset, int[] span) throws IOException {
+ String[] ret = new String[1];
+ JavaSource source = JavaSource.forDocument(doc);
+ if (source != null) {
+ source.runUserActionTask(controller -> {
+ controller.toPhase(JavaSource.Phase.ELEMENTS_RESOLVED);
+ TreeUtilities tu = controller.getTreeUtilities();
+ SourcePositions sp = controller.getTrees().getSourcePositions();
+ TreePath path = tu.pathFor(offset);
+ LiteralTree literal = null;
+ AnnotationTree annotation = null;
+ if (path.getLeaf().getKind() == Tree.Kind.STRING_LITERAL) {
+ literal = (LiteralTree) path.getLeaf();
+ TreePath annPath = tu.getPathElementOfKind(Tree.Kind.ANNOTATION, path);
+ annotation = annPath != null ? (AnnotationTree) annPath.getLeaf() : null;
+ } else if (path.getLeaf().getKind() == Tree.Kind.ANNOTATION) {
+ annotation = (AnnotationTree) path.getLeaf();
+ for (ExpressionTree arg : annotation.getArguments()) {
+ if (arg.getKind() == Tree.Kind.ASSIGNMENT) {
+ ExpressionTree expression = ((AssignmentTree) arg).getExpression();
+ if (expression.getKind() == Tree.Kind.STRING_LITERAL && sp.getStartPosition(path.getCompilationUnit(), expression) < offset && sp.getEndPosition(path.getCompilationUnit(), expression) >= offset) {
+ literal = (LiteralTree) expression;
+ }
+ }
+ }
+ }
+ if (literal != null && annotation != null) {
+ Element el = controller.getTrees().getElement(new TreePath(path, annotation.getAnnotationType()));
+ if (el != null && el.asType().getKind() == TypeKind.DECLARED) {
+ Name name = ((TypeElement)((DeclaredType)el.asType()).asElement()).getQualifiedName();
+ if ("io.micronaut.context.annotation.Property".contentEquals(name)) {
+ ret[0] = (String) literal.getValue();
+ if (span != null) {
+ span[0] = (int) sp.getStartPosition(path.getCompilationUnit(), literal) + 1;
+ span[1] = (int) sp.getEndPosition(path.getCompilationUnit(), literal) - 1;
+ }
+ } else if ("io.micronaut.context.annotation.Value".contentEquals(name)) {
+ String value = (String) literal.getValue();
+ Matcher matcher = INJECT.matcher(value);
+ if (matcher.find()) {
+ ret[0] = matcher.group(1);
+ if (span != null) {
+ span[0] = (int) sp.getStartPosition(path.getCompilationUnit(), literal) + matcher.start(1) + 1;
+ span[1] = (int) sp.getStartPosition(path.getCompilationUnit(), literal) + matcher.end(1) + 1;
+ }
+ }
+ }
+ }
+ }
+ }, true);
+ }
+ return ret[0];
+ }
+
+ @MimeRegistration(mimeType = "text/x-java", service = HyperlinkLocationProvider.class)
+ public static class LocationProvider implements HyperlinkLocationProvider {
+
+ @Override
+ public CompletableFuture<HyperlinkLocation> getHyperlinkLocation(Document doc, int offset) {
+ MicronautConfigUtilities.Usage usage = resolve(doc, offset, null);
+ return CompletableFuture.completedFuture(usage != null
+ ? HyperlinkLocationProvider.createHyperlinkLocation(usage.getFileObject(), usage.getStartOffset(), usage.getEndOffset())
+ : null);
+ }
+ }
+}
diff --git a/enterprise/micronaut/src/org/netbeans/modules/micronaut/newproject/BasePropertiesVisual.form b/enterprise/micronaut/src/org/netbeans/modules/micronaut/newproject/BasePropertiesVisual.form
new file mode 100644
index 0000000..f9546a8
--- /dev/null
+++ b/enterprise/micronaut/src/org/netbeans/modules/micronaut/newproject/BasePropertiesVisual.form
@@ -0,0 +1,348 @@
+<?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.
+
+-->
+
+<Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
+ <NonVisualComponents>
+ <Component class="javax.swing.ButtonGroup" name="serviceButtonGroup">
+ </Component>
+ <Component class="javax.swing.ButtonGroup" name="languageButtonGroup">
+ </Component>
+ </NonVisualComponents>
+ <AuxValues>
+ <AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
+ <AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
+ <AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
+ <AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
+ <AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
+ <AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
+ <AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
+ <AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
+ <AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
+ </AuxValues>
+
+ <Layout>
+ <DimensionLayout dim="0">
+ <Group type="103" groupAlignment="0" attributes="0">
+ <Group type="102" attributes="0">
+ <EmptySpace max="-2" attributes="0"/>
+ <Group type="103" groupAlignment="0" attributes="0">
+ <Component id="serviceURLLabel" min="-2" max="-2" attributes="0"/>
+ <Group type="102" attributes="0">
+ <Group type="103" groupAlignment="0" attributes="0">
+ <Component id="javaVersionLabel" alignment="0" min="-2" max="-2" attributes="0"/>
+ <Component id="groupLabel" alignment="0" min="-2" max="-2" attributes="0"/>
+ <Component id="languageLabel" alignment="0" min="-2" max="-2" attributes="0"/>
+ <Component id="customRadioButton" alignment="0" min="-2" max="-2" attributes="0"/>
+ <Component id="artifactLabel" alignment="0" min="-2" max="-2" attributes="0"/>
+ <Component id="applicationTypeLabel" alignment="0" min="-2" pref="90" max="-2" attributes="0"/>
+ <Component id="versionLabel" alignment="0" min="-2" max="-2" attributes="0"/>
+ <Component id="snapshotRadioButton" alignment="0" min="-2" max="-2" attributes="0"/>
+ <Component id="defaultRadioButton" alignment="0" min="-2" max="-2" attributes="0"/>
+ </Group>
+ <EmptySpace max="-2" attributes="0"/>
+ <Group type="103" groupAlignment="0" attributes="0">
+ <Component id="defaultURLLabel" max="32767" attributes="0"/>
+ <Component id="versionTextField" pref="566" max="32767" attributes="0"/>
+ <Component id="snapshotURLLabel" alignment="0" max="32767" attributes="0"/>
+ <Component id="customTextField" alignment="0" max="32767" attributes="0"/>
+ <Component id="applicationTypeComboBox" alignment="1" max="32767" attributes="0"/>
+ <Component id="artifactTextField" alignment="1" max="32767" attributes="0"/>
+ <Component id="groupTextField" max="32767" attributes="0"/>
+ <Group type="102" attributes="0">
+ <Group type="103" groupAlignment="0" attributes="0">
+ <Component id="javaVersionComboBox" min="-2" pref="63" max="-2" attributes="0"/>
+ <Group type="102" alignment="0" attributes="0">
+ <Component id="javaRadioButton" min="-2" pref="50" max="-2" attributes="0"/>
+ <EmptySpace type="unrelated" max="-2" attributes="0"/>
+ <Component id="kotlinRadioButton" min="-2" max="-2" attributes="0"/>
+ <EmptySpace type="unrelated" max="-2" attributes="0"/>
+ <Component id="groovyRadioButton" min="-2" max="-2" attributes="0"/>
+ </Group>
+ </Group>
+ <EmptySpace min="0" pref="0" max="32767" attributes="0"/>
+ </Group>
+ </Group>
+ </Group>
+ </Group>
+ <EmptySpace max="-2" attributes="0"/>
+ </Group>
+ </Group>
+ </DimensionLayout>
+ <DimensionLayout dim="1">
+ <Group type="103" groupAlignment="0" attributes="0">
+ <Group type="102" alignment="1" attributes="0">
+ <EmptySpace max="-2" attributes="0"/>
+ <Component id="serviceURLLabel" min="-2" max="-2" attributes="0"/>
+ <EmptySpace max="-2" attributes="0"/>
+ <Group type="103" groupAlignment="3" attributes="0">
+ <Component id="defaultRadioButton" alignment="3" min="-2" max="-2" attributes="0"/>
+ <Component id="defaultURLLabel" alignment="3" min="-2" max="-2" attributes="0"/>
+ </Group>
+ <EmptySpace max="-2" attributes="0"/>
+ <Group type="103" groupAlignment="3" attributes="0">
+ <Component id="snapshotRadioButton" alignment="3" min="-2" max="-2" attributes="0"/>
+ <Component id="snapshotURLLabel" alignment="0" min="-2" max="-2" attributes="0"/>
+ </Group>
+ <EmptySpace max="-2" attributes="0"/>
+ <Group type="103" groupAlignment="3" attributes="0">
+ <Component id="customRadioButton" alignment="3" min="-2" max="-2" attributes="0"/>
+ <Component id="customTextField" alignment="3" min="-2" max="-2" attributes="0"/>
+ </Group>
+ <EmptySpace max="-2" attributes="0"/>
+ <Group type="103" groupAlignment="3" attributes="0">
+ <Component id="versionTextField" alignment="3" min="-2" max="-2" attributes="0"/>
+ <Component id="versionLabel" alignment="3" min="-2" max="-2" attributes="0"/>
+ </Group>
+ <EmptySpace type="separate" max="-2" attributes="0"/>
+ <Group type="103" groupAlignment="3" attributes="0">
+ <Component id="groupTextField" alignment="3" min="-2" pref="19" max="-2" attributes="0"/>
+ <Component id="groupLabel" alignment="3" min="-2" max="-2" attributes="0"/>
+ </Group>
+ <EmptySpace max="-2" attributes="0"/>
+ <Group type="103" groupAlignment="3" attributes="0">
+ <Component id="artifactTextField" alignment="3" min="-2" max="-2" attributes="0"/>
+ <Component id="artifactLabel" alignment="3" min="-2" max="-2" attributes="0"/>
+ </Group>
+ <EmptySpace type="separate" max="-2" attributes="0"/>
+ <Group type="103" groupAlignment="3" attributes="0">
+ <Component id="applicationTypeComboBox" alignment="3" min="-2" max="-2" attributes="0"/>
+ <Component id="applicationTypeLabel" alignment="3" min="-2" max="-2" attributes="0"/>
+ </Group>
+ <EmptySpace max="-2" attributes="0"/>
+ <Group type="103" groupAlignment="3" attributes="0">
+ <Component id="javaVersionComboBox" alignment="3" min="-2" max="-2" attributes="0"/>
+ <Component id="javaVersionLabel" alignment="3" min="-2" max="-2" attributes="0"/>
+ </Group>
+ <EmptySpace min="-2" pref="4" max="-2" attributes="0"/>
+ <Group type="103" groupAlignment="3" attributes="0">
+ <Component id="languageLabel" alignment="3" min="-2" max="-2" attributes="0"/>
+ <Component id="javaRadioButton" alignment="3" min="-2" max="-2" attributes="0"/>
+ <Component id="kotlinRadioButton" alignment="3" min="-2" max="-2" attributes="0"/>
+ <Component id="groovyRadioButton" alignment="3" min="-2" max="-2" attributes="0"/>
+ </Group>
+ <EmptySpace pref="36" max="32767" attributes="0"/>
+ </Group>
+ </Group>
+ </DimensionLayout>
+ </Layout>
+ <SubComponents>
+ <Component class="javax.swing.JLabel" name="serviceURLLabel">
+ <Properties>
+ <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+ <ResourceString bundle="org/netbeans/modules/micronaut/newproject/Bundle.properties" key="LBL_ChooseServiceURL" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
+ </Property>
+ </Properties>
+ </Component>
+ <Component class="javax.swing.JRadioButton" name="defaultRadioButton">
+ <Properties>
+ <Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
+ <ComponentRef name="serviceButtonGroup"/>
+ </Property>
+ <Property name="selected" type="boolean" value="true"/>
+ <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+ <ResourceString bundle="org/netbeans/modules/micronaut/newproject/Bundle.properties" key="LBL_Default" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
+ </Property>
+ <Property name="actionCommand" type="java.lang.String" value="DEFAULT"/>
+ </Properties>
+ </Component>
+ <Component class="javax.swing.JLabel" name="defaultURLLabel">
+ <Properties>
+ <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
+ <Font name="Dialog" size="12" style="2"/>
+ </Property>
+ <Property name="text" type="java.lang.String" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
+ <Connection code="MicronautLaunchService.defaultURL" type="code"/>
+ </Property>
+ </Properties>
+ </Component>
+ <Component class="javax.swing.JRadioButton" name="snapshotRadioButton">
+ <Properties>
+ <Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
+ <ComponentRef name="serviceButtonGroup"/>
+ </Property>
+ <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+ <ResourceString bundle="org/netbeans/modules/micronaut/newproject/Bundle.properties" key="LBL_Snapshot" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
+ </Property>
+ <Property name="actionCommand" type="java.lang.String" value="SNAPSHOT"/>
+ </Properties>
+ </Component>
+ <Component class="javax.swing.JLabel" name="snapshotURLLabel">
+ <Properties>
+ <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
+ <Font name="Dialog" size="12" style="2"/>
+ </Property>
+ <Property name="text" type="java.lang.String" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
+ <Connection code="MicronautLaunchService.snapshotURL" type="code"/>
+ </Property>
+ </Properties>
+ </Component>
+ <Component class="javax.swing.JRadioButton" name="customRadioButton">
+ <Properties>
+ <Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
+ <ComponentRef name="serviceButtonGroup"/>
+ </Property>
+ <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+ <ResourceString bundle="org/netbeans/modules/micronaut/newproject/Bundle.properties" key="LBL_Custom" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
+ </Property>
+ <Property name="actionCommand" type="java.lang.String" value="CUSTOM"/>
+ </Properties>
+ </Component>
+ <Component class="javax.swing.JTextField" name="customTextField">
+ </Component>
+ <Component class="javax.swing.JLabel" name="versionLabel">
+ <Properties>
+ <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+ <ResourceString bundle="org/netbeans/modules/micronaut/newproject/Bundle.properties" key="LBL_Version" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
+ </Property>
+ </Properties>
+ </Component>
+ <Component class="javax.swing.JTextField" name="versionTextField">
+ <Properties>
+ <Property name="editable" type="boolean" value="false"/>
+ <Property name="columns" type="int" value="20"/>
+ </Properties>
+ </Component>
+ <Component class="javax.swing.JLabel" name="groupLabel">
+ <Properties>
+ <Property name="labelFor" type="java.awt.Component" editor="org.netbeans.modules.form.ComponentChooserEditor">
+ <ComponentRef name="groupTextField"/>
+ </Property>
+ <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+ <ResourceString bundle="org/netbeans/modules/micronaut/newproject/Bundle.properties" key="LBL_Group" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
+ </Property>
+ </Properties>
+ <AuxValues>
+ <AuxValue name="generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
+ </AuxValues>
+ </Component>
+ <Component class="javax.swing.JTextField" name="groupTextField">
+ <Properties>
+ <Property name="columns" type="int" value="20"/>
+ </Properties>
+ </Component>
+ <Component class="javax.swing.JLabel" name="artifactLabel">
+ <Properties>
+ <Property name="labelFor" type="java.awt.Component" editor="org.netbeans.modules.form.ComponentChooserEditor">
+ <ComponentRef name="artifactTextField"/>
+ </Property>
+ <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+ <ResourceString bundle="org/netbeans/modules/micronaut/newproject/Bundle.properties" key="LBL_Artifact" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
+ </Property>
+ </Properties>
+ <AuxValues>
+ <AuxValue name="generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
+ </AuxValues>
+ </Component>
+ <Component class="javax.swing.JTextField" name="artifactTextField">
+ <Properties>
+ <Property name="editable" type="boolean" value="false"/>
+ <Property name="columns" type="int" value="20"/>
+ </Properties>
+ </Component>
+ <Component class="javax.swing.JLabel" name="applicationTypeLabel">
+ <Properties>
+ <Property name="labelFor" type="java.awt.Component" editor="org.netbeans.modules.form.ComponentChooserEditor">
+ <ComponentRef name="applicationTypeComboBox"/>
+ </Property>
+ <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+ <ResourceString bundle="org/netbeans/modules/micronaut/newproject/Bundle.properties" key="LBL_ApplicationType" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
+ </Property>
+ </Properties>
+ </Component>
+ <Component class="javax.swing.JComboBox" name="applicationTypeComboBox">
+ <AuxValues>
+ <AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="<MicronautLaunchService.ApplicationType>"/>
+ </AuxValues>
+ </Component>
+ <Component class="javax.swing.JLabel" name="javaVersionLabel">
+ <Properties>
+ <Property name="labelFor" type="java.awt.Component" editor="org.netbeans.modules.form.ComponentChooserEditor">
+ <ComponentRef name="javaVersionComboBox"/>
+ </Property>
+ <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+ <ResourceString bundle="org/netbeans/modules/micronaut/newproject/Bundle.properties" key="LBL_JavaVersion" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
+ </Property>
+ </Properties>
+ </Component>
+ <Component class="javax.swing.JComboBox" name="javaVersionComboBox">
+ <Properties>
+ <Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.editors2.ComboBoxModelEditor">
+ <StringArray count="8">
+ <StringItem index="0" value="8"/>
+ <StringItem index="1" value="9"/>
+ <StringItem index="2" value="10"/>
+ <StringItem index="3" value="11"/>
+ <StringItem index="4" value="12"/>
+ <StringItem index="5" value="13"/>
+ <StringItem index="6" value="14"/>
+ <StringItem index="7" value="15"/>
+ </StringArray>
+ </Property>
+ </Properties>
+ <AuxValues>
+ <AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="<String>"/>
+ </AuxValues>
+ </Component>
+ <Component class="javax.swing.JLabel" name="languageLabel">
+ <Properties>
+ <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+ <ResourceString bundle="org/netbeans/modules/micronaut/newproject/Bundle.properties" key="LBL_Language" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
+ </Property>
+ </Properties>
+ </Component>
+ <Component class="javax.swing.JRadioButton" name="javaRadioButton">
+ <Properties>
+ <Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
+ <ComponentRef name="languageButtonGroup"/>
+ </Property>
+ <Property name="selected" type="boolean" value="true"/>
+ <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+ <ResourceString bundle="org/netbeans/modules/micronaut/newproject/Bundle.properties" key="LBL_Java" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
+ </Property>
+ <Property name="actionCommand" type="java.lang.String" value="JAVA"/>
+ </Properties>
+ </Component>
+ <Component class="javax.swing.JRadioButton" name="kotlinRadioButton">
+ <Properties>
+ <Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
+ <ComponentRef name="languageButtonGroup"/>
+ </Property>
+ <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+ <ResourceString bundle="org/netbeans/modules/micronaut/newproject/Bundle.properties" key="LBL_Kotlin" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
+ </Property>
+ <Property name="actionCommand" type="java.lang.String" value="KOTLIN"/>
+ </Properties>
+ </Component>
+ <Component class="javax.swing.JRadioButton" name="groovyRadioButton">
+ <Properties>
+ <Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
+ <ComponentRef name="languageButtonGroup"/>
+ </Property>
+ <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+ <ResourceString bundle="org/netbeans/modules/micronaut/newproject/Bundle.properties" key="LBL_Groovy" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
+ </Property>
+ <Property name="actionCommand" type="java.lang.String" value="GROOVY"/>
+ </Properties>
+ </Component>
+ </SubComponents>
+</Form>
diff --git a/enterprise/micronaut/src/org/netbeans/modules/micronaut/newproject/BasePropertiesVisual.java b/enterprise/micronaut/src/org/netbeans/modules/micronaut/newproject/BasePropertiesVisual.java
new file mode 100644
index 0000000..31674cb
--- /dev/null
+++ b/enterprise/micronaut/src/org/netbeans/modules/micronaut/newproject/BasePropertiesVisual.java
@@ -0,0 +1,424 @@
+/*
+ * 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.newproject;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.FocusAdapter;
+import java.awt.event.FocusEvent;
+import java.net.MalformedURLException;
+import java.net.URL;
+import javax.lang.model.SourceVersion;
+import javax.swing.JPanel;
+import javax.swing.SwingUtilities;
+import javax.swing.event.DocumentEvent;
+import javax.swing.event.DocumentListener;
+import org.openide.WizardDescriptor;
+import org.openide.WizardValidationException;
+import org.openide.util.AsyncGUIJob;
+import org.openide.util.RequestProcessor;
+
+/**
+ * Options panel for the Base Properties step in Micronaut project wizard.
+ *
+ * @author Dusan Balek
+ */
+public class BasePropertiesVisual extends JPanel implements DocumentListener, ActionListener, AsyncGUIJob {
+
+ private final BasePropertiesWizardPanel panel;
+ private boolean initialized = false;
+ private boolean failed = false;
+
+ public BasePropertiesVisual(BasePropertiesWizardPanel panel) {
+ initComponents();
+ this.panel = panel;
+ this.defaultRadioButton.addActionListener(this);
+ this.snapshotRadioButton.addActionListener(this);
+ this.customRadioButton.addActionListener(this);
+ this.customTextField.getDocument().addDocumentListener(this);
+ this.groupTextField.getDocument().addDocumentListener(this);
+ this.versionTextField.getDocument().addDocumentListener(this);
+ this.customTextField.addFocusListener(new FocusAdapter() {
+ @Override
+ public void focusGained(FocusEvent e) {
+ customRadioButton.setSelected(true);
+ versionTextField.setText("");
+ applicationTypeComboBox.removeAllItems();
+ }
+ @Override
+ public void focusLost(FocusEvent e) {
+ if (customRadioButton.isSelected()) {
+ if (customURLValid()) {
+ refresh();
+ } else {
+ panel.fireChangeEvent();
+ }
+ }
+ }
+ });
+ }
+
+ /**
+ * This method is called from within the constructor to initialize the form. WARNING: Do NOT modify this code. The content of
+ * this method is always regenerated by the Form Editor.
+ */
+ // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
+ private void initComponents() {
+
+ serviceButtonGroup = new javax.swing.ButtonGroup();
+ languageButtonGroup = new javax.swing.ButtonGroup();
+ serviceURLLabel = new javax.swing.JLabel();
+ defaultRadioButton = new javax.swing.JRadioButton();
+ defaultURLLabel = new javax.swing.JLabel();
+ snapshotRadioButton = new javax.swing.JRadioButton();
+ snapshotURLLabel = new javax.swing.JLabel();
+ customRadioButton = new javax.swing.JRadioButton();
+ customTextField = new javax.swing.JTextField();
+ versionLabel = new javax.swing.JLabel();
+ versionTextField = new javax.swing.JTextField();
+ groupLabel = new javax.swing.JLabel();
+ groupTextField = new javax.swing.JTextField();
+ artifactLabel = new javax.swing.JLabel();
+ artifactTextField = new javax.swing.JTextField();
+ applicationTypeLabel = new javax.swing.JLabel();
+ applicationTypeComboBox = new javax.swing.JComboBox<>();
+ javaVersionLabel = new javax.swing.JLabel();
+ javaVersionComboBox = new javax.swing.JComboBox<>();
+ languageLabel = new javax.swing.JLabel();
+ javaRadioButton = new javax.swing.JRadioButton();
+ kotlinRadioButton = new javax.swing.JRadioButton();
+ groovyRadioButton = new javax.swing.JRadioButton();
+
+ org.openide.awt.Mnemonics.setLocalizedText(serviceURLLabel, org.openide.util.NbBundle.getMessage(BasePropertiesVisual.class, "LBL_ChooseServiceURL")); // NOI18N
+
+ serviceButtonGroup.add(defaultRadioButton);
+ defaultRadioButton.setSelected(true);
+ org.openide.awt.Mnemonics.setLocalizedText(defaultRadioButton, org.openide.util.NbBundle.getMessage(BasePropertiesVisual.class, "LBL_Default")); // NOI18N
+ defaultRadioButton.setActionCommand("DEFAULT");
+
+ defaultURLLabel.setFont(new java.awt.Font("Dialog", 2, 12)); // NOI18N
+ org.openide.awt.Mnemonics.setLocalizedText(defaultURLLabel, MicronautLaunchService.defaultURL);
+
+ serviceButtonGroup.add(snapshotRadioButton);
+ org.openide.awt.Mnemonics.setLocalizedText(snapshotRadioButton, org.openide.util.NbBundle.getMessage(BasePropertiesVisual.class, "LBL_Snapshot")); // NOI18N
+ snapshotRadioButton.setActionCommand("SNAPSHOT");
+
+ snapshotURLLabel.setFont(new java.awt.Font("Dialog", 2, 12)); // NOI18N
+ org.openide.awt.Mnemonics.setLocalizedText(snapshotURLLabel, MicronautLaunchService.snapshotURL);
+
+ serviceButtonGroup.add(customRadioButton);
+ org.openide.awt.Mnemonics.setLocalizedText(customRadioButton, org.openide.util.NbBundle.getMessage(BasePropertiesVisual.class, "LBL_Custom")); // NOI18N
+ customRadioButton.setActionCommand("CUSTOM");
+
+ org.openide.awt.Mnemonics.setLocalizedText(versionLabel, org.openide.util.NbBundle.getMessage(BasePropertiesVisual.class, "LBL_Version")); // NOI18N
+
+ versionTextField.setEditable(false);
+ versionTextField.setColumns(20);
+
+ groupLabel.setLabelFor(groupTextField);
+ org.openide.awt.Mnemonics.setLocalizedText(groupLabel, org.openide.util.NbBundle.getMessage(BasePropertiesVisual.class, "LBL_Group")); // NOI18N
+
+ groupTextField.setColumns(20);
+
+ artifactLabel.setLabelFor(artifactTextField);
+ org.openide.awt.Mnemonics.setLocalizedText(artifactLabel, org.openide.util.NbBundle.getMessage(BasePropertiesVisual.class, "LBL_Artifact")); // NOI18N
+
+ artifactTextField.setEditable(false);
+ artifactTextField.setColumns(20);
+
+ applicationTypeLabel.setLabelFor(applicationTypeComboBox);
+ org.openide.awt.Mnemonics.setLocalizedText(applicationTypeLabel, org.openide.util.NbBundle.getMessage(BasePropertiesVisual.class, "LBL_ApplicationType")); // NOI18N
+
+ javaVersionLabel.setLabelFor(javaVersionComboBox);
+ org.openide.awt.Mnemonics.setLocalizedText(javaVersionLabel, org.openide.util.NbBundle.getMessage(BasePropertiesVisual.class, "LBL_JavaVersion")); // NOI18N
+
+ javaVersionComboBox.setModel(new javax.swing.DefaultComboBoxModel<>(new String[] { "8", "9", "10", "11", "12", "13", "14", "15" }));
+
+ org.openide.awt.Mnemonics.setLocalizedText(languageLabel, org.openide.util.NbBundle.getMessage(BasePropertiesVisual.class, "LBL_Language")); // NOI18N
+
+ languageButtonGroup.add(javaRadioButton);
+ javaRadioButton.setSelected(true);
+ org.openide.awt.Mnemonics.setLocalizedText(javaRadioButton, org.openide.util.NbBundle.getMessage(BasePropertiesVisual.class, "LBL_Java")); // NOI18N
+ javaRadioButton.setActionCommand("JAVA");
+
+ languageButtonGroup.add(kotlinRadioButton);
+ org.openide.awt.Mnemonics.setLocalizedText(kotlinRadioButton, org.openide.util.NbBundle.getMessage(BasePropertiesVisual.class, "LBL_Kotlin")); // NOI18N
+ kotlinRadioButton.setActionCommand("KOTLIN");
+
+ languageButtonGroup.add(groovyRadioButton);
+ org.openide.awt.Mnemonics.setLocalizedText(groovyRadioButton, org.openide.util.NbBundle.getMessage(BasePropertiesVisual.class, "LBL_Groovy")); // NOI18N
+ groovyRadioButton.setActionCommand("GROOVY");
+
+ javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
+ this.setLayout(layout);
+ layout.setHorizontalGroup(
+ layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(layout.createSequentialGroup()
+ .addContainerGap()
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(serviceURLLabel)
+ .addGroup(layout.createSequentialGroup()
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(javaVersionLabel)
+ .addComponent(groupLabel)
+ .addComponent(languageLabel)
+ .addComponent(customRadioButton)
+ .addComponent(artifactLabel)
+ .addComponent(applicationTypeLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 90, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(versionLabel)
+ .addComponent(snapshotRadioButton)
+ .addComponent(defaultRadioButton))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(defaultURLLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addComponent(versionTextField, javax.swing.GroupLayout.DEFAULT_SIZE, 566, Short.MAX_VALUE)
+ .addComponent(snapshotURLLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addComponent(customTextField)
+ .addComponent(applicationTypeComboBox, javax.swing.GroupLayout.Alignment.TRAILING, 0, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addComponent(artifactTextField, javax.swing.GroupLayout.Alignment.TRAILING)
+ .addComponent(groupTextField)
+ .addGroup(layout.createSequentialGroup()
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(javaVersionComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, 63, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addGroup(layout.createSequentialGroup()
+ .addComponent(javaRadioButton, javax.swing.GroupLayout.PREFERRED_SIZE, 50, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
+ .addComponent(kotlinRadioButton)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
+ .addComponent(groovyRadioButton)))
+ .addGap(0, 0, Short.MAX_VALUE)))))
+ .addContainerGap())
+ );
+ layout.setVerticalGroup(
+ layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
+ .addContainerGap()
+ .addComponent(serviceURLLabel)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(defaultRadioButton)
+ .addComponent(defaultURLLabel))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(snapshotRadioButton)
+ .addComponent(snapshotURLLabel))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(customRadioButton)
+ .addComponent(customTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(versionTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(versionLabel))
+ .addGap(18, 18, 18)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(groupTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 19, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(groupLabel))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(artifactTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(artifactLabel))
+ .addGap(18, 18, 18)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(applicationTypeComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(applicationTypeLabel))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(javaVersionComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(javaVersionLabel))
+ .addGap(4, 4, 4)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(languageLabel)
+ .addComponent(javaRadioButton)
+ .addComponent(kotlinRadioButton)
+ .addComponent(groovyRadioButton))
+ .addContainerGap(36, Short.MAX_VALUE))
+ );
+ }// </editor-fold>//GEN-END:initComponents
+
+ // Variables declaration - do not modify//GEN-BEGIN:variables
+ private javax.swing.JComboBox<MicronautLaunchService.ApplicationType> applicationTypeComboBox;
+ private javax.swing.JLabel applicationTypeLabel;
+ private javax.swing.JLabel artifactLabel;
+ private javax.swing.JTextField artifactTextField;
+ private javax.swing.JRadioButton customRadioButton;
+ private javax.swing.JTextField customTextField;
+ private javax.swing.JRadioButton defaultRadioButton;
+ private javax.swing.JLabel defaultURLLabel;
+ private javax.swing.JRadioButton groovyRadioButton;
+ private javax.swing.JLabel groupLabel;
+ private javax.swing.JTextField groupTextField;
+ private javax.swing.JRadioButton javaRadioButton;
+ private javax.swing.JComboBox<String> javaVersionComboBox;
+ private javax.swing.JLabel javaVersionLabel;
+ private javax.swing.JRadioButton kotlinRadioButton;
+ private javax.swing.ButtonGroup languageButtonGroup;
+ private javax.swing.JLabel languageLabel;
+ private javax.swing.ButtonGroup serviceButtonGroup;
+ private javax.swing.JLabel serviceURLLabel;
+ private javax.swing.JRadioButton snapshotRadioButton;
+ private javax.swing.JLabel snapshotURLLabel;
+ private javax.swing.JLabel versionLabel;
+ private javax.swing.JTextField versionTextField;
+ // End of variables declaration//GEN-END:variables
+
+ boolean valid(WizardDescriptor wizardDescriptor) {
+ if (failed) {
+ wizardDescriptor.putProperty(WizardDescriptor.PROP_ERROR_MESSAGE, "Problems in contacting service!");
+ return false;
+ }
+ if (groupTextField.getText().isEmpty()) {
+ wizardDescriptor.putProperty(WizardDescriptor.PROP_ERROR_MESSAGE, "Group can't be empty.");
+ return false;
+ }
+ if (!SourceVersion.isName(groupTextField.getText())) {
+ wizardDescriptor.putProperty(WizardDescriptor.PROP_ERROR_MESSAGE, "Not a valid Group name.");
+ return false;
+ }
+ if (customRadioButton.isSelected()) {
+ if (customTextField.getText().isEmpty()) {
+ wizardDescriptor.putProperty(WizardDescriptor.PROP_ERROR_MESSAGE, "Service URL can't be empty.");
+ return false;
+ }
+ if (!customURLValid()) {
+ wizardDescriptor.putProperty(WizardDescriptor.PROP_ERROR_MESSAGE, "Not a valid Service URL.");
+ return false;
+ }
+ }
+ wizardDescriptor.putProperty(WizardDescriptor.PROP_ERROR_MESSAGE, "");
+ return !versionTextField.getText().isEmpty();
+ }
+
+ void store(WizardDescriptor wd) {
+ wd.putProperty(MicronautProjectWizardIterator.SERVICE_URL, getServiceUrl());
+ wd.putProperty(MicronautProjectWizardIterator.MAVEN_GROUP, groupTextField.getText().trim());
+ wd.putProperty(MicronautProjectWizardIterator.APPLICATION_TYPE, applicationTypeComboBox.getSelectedItem());
+ wd.putProperty(MicronautProjectWizardIterator.JAVA_VERSION, javaVersionComboBox.getSelectedItem());
+ wd.putProperty(MicronautProjectWizardIterator.LANGUAGE, languageButtonGroup.getSelection().getActionCommand());
+ }
+
+ void read(WizardDescriptor wd) {
+ String serviceUrl = (String) wd.getProperty(MicronautProjectWizardIterator.SERVICE_URL);
+ if (serviceUrl != null) {
+ switch (serviceUrl) {
+ case MicronautLaunchService.defaultURL:
+ this.defaultRadioButton.setSelected(true);
+ break;
+ case MicronautLaunchService.snapshotURL:
+ this.snapshotRadioButton.setSelected(true);
+ break;
+ default:
+ this.customTextField.setText(serviceUrl);
+ }
+ }
+ String group = (String) wd.getProperty(MicronautProjectWizardIterator.MAVEN_GROUP);
+ this.groupTextField.setText(group == null || group.isEmpty() ? "com.example" : group);
+ this.artifactTextField.setText((String) wd.getProperty(MicronautProjectWizardIterator.MAVEN_ARTIFACT));
+ javaVersionComboBox.setSelectedItem(MicronautProjectWizardIterator.JAVA_VERSION);
+ applicationTypeComboBox.setSelectedItem(wd.getProperty(MicronautProjectWizardIterator.APPLICATION_TYPE));
+ }
+
+ void validate(WizardDescriptor d) throws WizardValidationException {
+ // nothing to validate
+ }
+
+ @Override
+ public void insertUpdate(DocumentEvent e) {
+ panel.fireChangeEvent();
+ }
+
+ @Override
+ public void removeUpdate(DocumentEvent e) {
+ panel.fireChangeEvent();
+ }
+
+ @Override
+ public void changedUpdate(DocumentEvent e) {
+ panel.fireChangeEvent();
+ }
+
+ @Override
+ public void construct() {
+ try {
+ panel.wizardDescriptor.putProperty(WizardDescriptor.PROP_INFO_MESSAGE, "Contacting service...");
+ MicronautLaunchService service = MicronautLaunchService.getInstance();
+ String serviceUrl = getServiceUrl();
+ String micronautVersion = service.getMicronautVersion(serviceUrl);
+ for (MicronautLaunchService.ApplicationType type : service.getApplicationTypes(serviceUrl)) {
+ applicationTypeComboBox.addItem(type);
+ }
+ versionTextField.setText(micronautVersion);
+ initialized = true;
+ } catch (Exception ex) {
+ panel.wizardDescriptor.putProperty(WizardDescriptor.PROP_ERROR_MESSAGE, "Could not query Micronaut Launch service");
+ failed = true;
+ panel.fireChangeEvent();
+ }
+ }
+
+ @Override
+ public void finished() {
+ if (initialized) {
+ panel.fireChangeEvent();
+ groupTextField.requestFocus();
+ }
+ }
+
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ versionTextField.setText("");
+ applicationTypeComboBox.removeAllItems();
+ if ("CUSTOM".equals(e.getActionCommand())) {
+ customTextField.requestFocus();
+ } else {
+ refresh();
+ }
+ }
+
+ private void refresh() {
+ initialized = false;
+ failed = false;
+ RequestProcessor.getDefault().post(() -> {
+ construct();
+ SwingUtilities.invokeLater(() -> {
+ finished();
+ });
+ });
+ }
+
+ private String getServiceUrl() {
+ switch (serviceButtonGroup.getSelection().getActionCommand()) {
+ case "DEFAULT":
+ return MicronautLaunchService.defaultURL;
+ case "SNAPSHOT":
+ return MicronautLaunchService.snapshotURL;
+ default:
+ return customTextField.getText();
+ }
+ }
+
+ private boolean customURLValid() {
+ try {
+ new URL(customTextField.getText());
+ } catch (MalformedURLException ex) {
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/enterprise/micronaut/src/org/netbeans/modules/micronaut/newproject/BasePropertiesWizardPanel.java b/enterprise/micronaut/src/org/netbeans/modules/micronaut/newproject/BasePropertiesWizardPanel.java
new file mode 100644
index 0000000..3fc91d0
--- /dev/null
+++ b/enterprise/micronaut/src/org/netbeans/modules/micronaut/newproject/BasePropertiesWizardPanel.java
@@ -0,0 +1,108 @@
+/*
+ * 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.newproject;
+
+import java.awt.Component;
+import javax.swing.event.ChangeListener;
+import org.openide.WizardDescriptor;
+import org.openide.WizardValidationException;
+import org.openide.util.ChangeSupport;
+import org.openide.util.HelpCtx;
+import org.openide.util.NbBundle;
+
+/**
+ * Wizard descriptor for Base Properties step in Micronaut project wizard.
+ *
+ * @author Dusan Balek
+ */
+public class BasePropertiesWizardPanel implements WizardDescriptor.ValidatingPanel<WizardDescriptor>, WizardDescriptor.FinishablePanel<WizardDescriptor> {
+
+ WizardDescriptor wizardDescriptor;
+ private BasePropertiesVisual component;
+
+ public BasePropertiesWizardPanel() {
+ }
+
+ @Override
+ @NbBundle.Messages("LBL_BasePropertiesStep=Base Properties")
+ public Component getComponent() {
+ if (component == null) {
+ component = new BasePropertiesVisual(this);
+ component.setName(Bundle.LBL_BasePropertiesStep());
+ }
+ return component;
+ }
+
+ @Override
+ public HelpCtx getHelp() {
+ // Show no Help button for this panel:
+ return HelpCtx.DEFAULT_HELP;
+ // If you have context help:
+ // return new HelpCtx("help.key.here");
+ }
+
+ @Override
+ public boolean isValid() {
+ getComponent();
+ return component.valid(wizardDescriptor);
+ }
+
+ private final ChangeSupport chgSupport = new ChangeSupport(this);
+
+ @Override
+ public final void addChangeListener(ChangeListener l) {
+ synchronized (chgSupport) {
+ chgSupport.addChangeListener(l);
+ }
+ }
+
+ @Override
+ public final void removeChangeListener(ChangeListener l) {
+ synchronized (chgSupport) {
+ chgSupport.removeChangeListener(l);
+ }
+ }
+
+ protected final void fireChangeEvent() {
+ chgSupport.fireChange();
+ }
+
+ @Override
+ public void readSettings(WizardDescriptor wiz) {
+ wizardDescriptor = wiz;
+ component.read(wizardDescriptor);
+ }
+
+ @Override
+ public void storeSettings(WizardDescriptor wiz) {
+ WizardDescriptor d = wiz;
+ component.store(d);
+ }
+
+ @Override
+ public boolean isFinishPanel() {
+ return true;
+ }
+
+ @Override
+ public void validate() throws WizardValidationException {
+ getComponent();
+ component.validate(wizardDescriptor);
+ }
+}
diff --git a/java/java.sourceui/nbproject/project.properties b/enterprise/micronaut/src/org/netbeans/modules/micronaut/newproject/Bundle.properties
similarity index 63%
copy from java/java.sourceui/nbproject/project.properties
copy to enterprise/micronaut/src/org/netbeans/modules/micronaut/newproject/Bundle.properties
index 01b9ec4..24c11d1 100644
--- a/java/java.sourceui/nbproject/project.properties
+++ b/enterprise/micronaut/src/org/netbeans/modules/micronaut/newproject/Bundle.properties
@@ -14,14 +14,21 @@
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
-javadoc.apichanges=${basedir}/apichanges.xml
-javac.compilerargs=-Xlint -Xlint:-serial
-javac.source=1.8
-javadoc.arch=${basedir}/arch.xml
-spec.version.base=1.57.0
-test.config.stableBTD.includes=**/*Test.class
-test.config.stableBTD.excludes=\
- **/ElementHeadersTest.class
-
-requires.nb.javac=true
+LBL_ProjectName=Project &Name\:
+LBL_ProjectLocation=Project &Location\:
+LBL_ProjectFolder=Project Fol&der:
+BTN_Browse=Br&owse...
+LBL_ChooseServiceURL=Choose service URL:
+LBL_Default=Default:
+LBL_Snapshot=Snapshot:
+LBL_Custom=Custom:
+LBL_Group=Group:
+LBL_Artifact=Artifact:
+LBL_Version=Micronaut version:
+LBL_ApplicationType=Application type:
+LBL_JavaVersion=Java version:
+LBL_Language=Language:
+LBL_Java=Java
+LBL_Kotlin=Kotlin
+LBL_Groovy=Groovy
diff --git a/enterprise/micronaut/src/org/netbeans/modules/micronaut/newproject/FeatureTogglePanel.form b/enterprise/micronaut/src/org/netbeans/modules/micronaut/newproject/FeatureTogglePanel.form
new file mode 100644
index 0000000..3a4d379
--- /dev/null
+++ b/enterprise/micronaut/src/org/netbeans/modules/micronaut/newproject/FeatureTogglePanel.form
@@ -0,0 +1,78 @@
+<?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.
+
+-->
+
+<Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
+ <AuxValues>
+ <AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
+ <AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
+ <AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
+ <AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
+ <AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
+ <AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
+ <AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
+ <AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
+ <AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
+ </AuxValues>
+
+ <Layout>
+ <DimensionLayout dim="0">
+ <Group type="103" groupAlignment="0" attributes="0">
+ <Component id="featureCheckBox" min="-2" max="-2" attributes="0"/>
+ <Group type="102" attributes="0">
+ <EmptySpace min="21" pref="21" max="-2" attributes="0"/>
+ <Component id="descriptionLabel" min="-2" max="-2" attributes="0"/>
+ </Group>
+ </Group>
+ </DimensionLayout>
+ <DimensionLayout dim="1">
+ <Group type="103" groupAlignment="0" attributes="0">
+ <Group type="102" alignment="0" attributes="0">
+ <Component id="featureCheckBox" min="-2" max="-2" attributes="0"/>
+ <EmptySpace max="-2" attributes="0"/>
+ <Component id="descriptionLabel" min="-2" max="-2" attributes="0"/>
+ </Group>
+ </Group>
+ </DimensionLayout>
+ </Layout>
+ <SubComponents>
+ <Component class="javax.swing.JCheckBox" name="featureCheckBox">
+ <Properties>
+ <Property name="text" type="java.lang.String" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
+ <Connection code="feature.getTitle()" type="code"/>
+ </Property>
+ </Properties>
+ </Component>
+ <Component class="javax.swing.JLabel" name="descriptionLabel">
+ <Properties>
+ <Property name="font" type="java.awt.Font" editor="org.netbeans.modules.form.editors2.FontEditor">
+ <FontInfo relative="true">
+ <Font bold="false" component="descriptionLabel" property="font" relativeSize="true" size="-1"/>
+ </FontInfo>
+ </Property>
+ <Property name="text" type="java.lang.String" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
+ <Connection code="feature.getDescription()" type="code"/>
+ </Property>
+ </Properties>
+ </Component>
+ </SubComponents>
+</Form>
diff --git a/enterprise/micronaut/src/org/netbeans/modules/micronaut/newproject/FeatureTogglePanel.java b/enterprise/micronaut/src/org/netbeans/modules/micronaut/newproject/FeatureTogglePanel.java
new file mode 100644
index 0000000..f8119af
--- /dev/null
+++ b/enterprise/micronaut/src/org/netbeans/modules/micronaut/newproject/FeatureTogglePanel.java
@@ -0,0 +1,89 @@
+/*
+ * 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.newproject;
+
+/**
+ *
+ * @author Dusan Balek
+ */
+public class FeatureTogglePanel extends javax.swing.JPanel {
+
+ private final MicronautLaunchService.Feature feature;
+
+ /**
+ * Creates new form FeatureTogglePanel
+ */
+ public FeatureTogglePanel(MicronautLaunchService.Feature feature) {
+ this.feature = feature;
+ initComponents();
+ }
+
+ public MicronautLaunchService.Feature getFeature() {
+ return feature;
+ }
+
+ public boolean isSelected() {
+ return featureCheckBox.isSelected();
+ }
+
+ public void setSelected(boolean b) {
+ featureCheckBox.setSelected(b);
+ }
+
+ /**
+ * This method is called from within the constructor to initialize the form.
+ * WARNING: Do NOT modify this code. The content of this method is always
+ * regenerated by the Form Editor.
+ */
+ @SuppressWarnings("unchecked")
+ // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
+ private void initComponents() {
+
+ featureCheckBox = new javax.swing.JCheckBox();
+ descriptionLabel = new javax.swing.JLabel();
+
+ org.openide.awt.Mnemonics.setLocalizedText(featureCheckBox, feature.getTitle());
+
+ descriptionLabel.setFont(descriptionLabel.getFont().deriveFont(descriptionLabel.getFont().getStyle() & ~java.awt.Font.BOLD, descriptionLabel.getFont().getSize()-1));
+ org.openide.awt.Mnemonics.setLocalizedText(descriptionLabel, feature.getDescription());
+
+ javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
+ this.setLayout(layout);
+ layout.setHorizontalGroup(
+ layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(featureCheckBox)
+ .addGroup(layout.createSequentialGroup()
+ .addGap(21, 21, 21)
+ .addComponent(descriptionLabel))
+ );
+ layout.setVerticalGroup(
+ layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(layout.createSequentialGroup()
+ .addComponent(featureCheckBox)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(descriptionLabel))
+ );
+ }// </editor-fold>//GEN-END:initComponents
+
+
+ // Variables declaration - do not modify//GEN-BEGIN:variables
+ private javax.swing.JLabel descriptionLabel;
+ private javax.swing.JCheckBox featureCheckBox;
+ // End of variables declaration//GEN-END:variables
+}
diff --git a/enterprise/micronaut/src/org/netbeans/modules/micronaut/newproject/FeaturesVisual.form b/enterprise/micronaut/src/org/netbeans/modules/micronaut/newproject/FeaturesVisual.form
new file mode 100644
index 0000000..195a3d4
--- /dev/null
+++ b/enterprise/micronaut/src/org/netbeans/modules/micronaut/newproject/FeaturesVisual.form
@@ -0,0 +1,64 @@
+<?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.
+
+-->
+
+<Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
+ <AuxValues>
+ <AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
+ <AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
+ <AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
+ <AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
+ <AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
+ <AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
+ <AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
+ <AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
+ <AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
+ </AuxValues>
+
+ <Layout>
+ <DimensionLayout dim="0">
+ <Group type="103" groupAlignment="0" attributes="0">
+ <Component id="scrollPane" alignment="0" pref="556" max="32767" attributes="0"/>
+ </Group>
+ </DimensionLayout>
+ <DimensionLayout dim="1">
+ <Group type="103" groupAlignment="0" attributes="0">
+ <Component id="scrollPane" alignment="0" pref="298" max="32767" attributes="0"/>
+ </Group>
+ </DimensionLayout>
+ </Layout>
+ <SubComponents>
+ <Container class="javax.swing.JScrollPane" name="scrollPane">
+ <Properties>
+ <Property name="horizontalScrollBarPolicy" type="int" value="31"/>
+ </Properties>
+
+ <Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
+ <SubComponents>
+ <Container class="javax.swing.JPanel" name="nestedPanel">
+
+ <Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
+ </Container>
+ </SubComponents>
+ </Container>
+ </SubComponents>
+</Form>
diff --git a/enterprise/micronaut/src/org/netbeans/modules/micronaut/newproject/FeaturesVisual.java b/enterprise/micronaut/src/org/netbeans/modules/micronaut/newproject/FeaturesVisual.java
new file mode 100644
index 0000000..39736bf
--- /dev/null
+++ b/enterprise/micronaut/src/org/netbeans/modules/micronaut/newproject/FeaturesVisual.java
@@ -0,0 +1,190 @@
+/*
+ * 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.newproject;
+
+import java.awt.Insets;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.SwingUtilities;
+
+import org.openide.WizardDescriptor;
+import org.openide.WizardValidationException;
+import org.openide.util.AsyncGUIJob;
+import org.openide.util.RequestProcessor;
+
+/**
+ * Options panel for Features step in Micronaut project wizard.
+ *
+ * @author Dusan Balek
+ */
+public class FeaturesVisual extends JPanel implements AsyncGUIJob {
+
+ private final FeaturesWizardPanel panel;
+ private boolean initialized = false;
+ private boolean failed = false;
+ private Map<String, List<FeatureTogglePanel>> featuresMap;
+
+ public FeaturesVisual(FeaturesWizardPanel panel) {
+ this.panel = panel;
+ initComponents();
+ }
+
+ @Override
+ public void addNotify() {
+ super.addNotify();
+ nestedPanel.requestFocus();
+ }
+
+ /** This method is called from within the constructor to initialize the form. WARNING: Do NOT modify this code. The content of
+ * this method is always regenerated by the Form Editor.
+ */
+ // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
+ private void initComponents() {
+ java.awt.GridBagConstraints gridBagConstraints;
+
+ scrollPane = new javax.swing.JScrollPane();
+ nestedPanel = new javax.swing.JPanel();
+
+ scrollPane.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
+
+ nestedPanel.setLayout(new java.awt.GridBagLayout());
+ scrollPane.setViewportView(nestedPanel);
+
+ javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
+ this.setLayout(layout);
+ layout.setHorizontalGroup(
+ layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(scrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 556, Short.MAX_VALUE)
+ );
+ layout.setVerticalGroup(
+ layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(scrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 298, Short.MAX_VALUE)
+ );
+ }// </editor-fold>//GEN-END:initComponents
+
+ // Variables declaration - do not modify//GEN-BEGIN:variables
+ private javax.swing.JPanel nestedPanel;
+ private javax.swing.JScrollPane scrollPane;
+ // End of variables declaration//GEN-END:variables
+
+ boolean valid(WizardDescriptor wizardDescriptor) {
+ if (failed) {
+ wizardDescriptor.putProperty(WizardDescriptor.PROP_ERROR_MESSAGE, "Problems in contacting service!");
+ return false;
+ }
+ wizardDescriptor.putProperty(WizardDescriptor.PROP_ERROR_MESSAGE, "");
+ return initialized;
+ }
+
+ void store(WizardDescriptor wd) {
+ Set<MicronautLaunchService.Feature> selectedFeatures = new LinkedHashSet<>();
+ for (List<FeatureTogglePanel> toggles : featuresMap.values()) {
+ for (FeatureTogglePanel toggle : toggles) {
+ if (toggle.isSelected()) {
+ selectedFeatures.add(toggle.getFeature());
+ }
+ }
+ }
+ wd.putProperty(MicronautProjectWizardIterator.FEATURES, selectedFeatures);
+ }
+
+ void read(WizardDescriptor wd) {
+ Set<MicronautLaunchService.Feature> selectedFeatures = (Set<MicronautLaunchService.Feature>) wd.getProperty(MicronautProjectWizardIterator.FEATURES);
+ if (selectedFeatures != null && !selectedFeatures.isEmpty()) {
+ for (List<FeatureTogglePanel> toggles : featuresMap.values()) {
+ for (FeatureTogglePanel toggle : toggles) {
+ toggle.setSelected(selectedFeatures.contains(toggle.getFeature()));
+ }
+ }
+ }
+ }
+
+ void validate(WizardDescriptor d) throws WizardValidationException {
+ // nothing to validate
+ }
+
+ @Override
+ public void construct() {
+ init();
+ panel.wizardDescriptor.addPropertyChangeListener((evt) -> {
+ if (evt.getPropertyName() == MicronautProjectWizardIterator.SERVICE_URL ||
+ evt.getPropertyName() == MicronautProjectWizardIterator.APPLICATION_TYPE) {
+ refresh();
+ }
+ });
+ }
+
+ private void init() {
+ try {
+ featuresMap = new LinkedHashMap<>();
+ panel.wizardDescriptor.putProperty(WizardDescriptor.PROP_INFO_MESSAGE, "Contacting service...");
+ String serviceUrl = (String) panel.wizardDescriptor.getProperty(MicronautProjectWizardIterator.SERVICE_URL);
+ MicronautLaunchService.ApplicationType appType = (MicronautLaunchService.ApplicationType) panel.wizardDescriptor.getProperty(MicronautProjectWizardIterator.APPLICATION_TYPE);
+ for (MicronautLaunchService.Feature feature : MicronautLaunchService.getInstance().getFeatures(serviceUrl, appType)) {
+ List<FeatureTogglePanel> toggles = featuresMap.computeIfAbsent(feature.getCategory(), (c) -> new ArrayList<>());
+ toggles.add(new FeatureTogglePanel(feature));
+ }
+ java.awt.GridBagConstraints gridBagConstraints = new java.awt.GridBagConstraints();
+ gridBagConstraints.gridx = 0;
+ gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
+ gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
+ gridBagConstraints.weightx = 1.0;
+ gridBagConstraints.insets = new Insets(4, 8, 0, 0);
+ int i = 0;
+ for (Map.Entry<String, List<FeatureTogglePanel>> entry : featuresMap.entrySet()) {
+ gridBagConstraints.gridy = i++;
+ JLabel label = new JLabel(entry.getKey());
+ label.setFont(label.getFont().deriveFont(java.awt.Font.BOLD));
+ nestedPanel.add(label, gridBagConstraints);
+ for (FeatureTogglePanel toggle : entry.getValue()) {
+ gridBagConstraints.gridy = i++;
+ nestedPanel.add(toggle, gridBagConstraints);
+ }
+ }
+ nestedPanel.revalidate();
+ initialized = true;
+ } catch (Exception ex) {
+ panel.wizardDescriptor.putProperty(WizardDescriptor.PROP_ERROR_MESSAGE, "Could not query Micronaut Launch service");
+ failed = true;
+ }
+ }
+
+ @Override
+ public void finished() {
+ panel.fireChangeEvent();
+ }
+
+ private void refresh() {
+ initialized = false;
+ failed = false;
+ nestedPanel.removeAll();
+ RequestProcessor.getDefault().post(() -> {
+ init();
+ SwingUtilities.invokeLater(() -> {
+ finished();
+ });
+ });
+ }
+}
diff --git a/enterprise/micronaut/src/org/netbeans/modules/micronaut/newproject/FeaturesWizardPanel.java b/enterprise/micronaut/src/org/netbeans/modules/micronaut/newproject/FeaturesWizardPanel.java
new file mode 100644
index 0000000..cea4597
--- /dev/null
+++ b/enterprise/micronaut/src/org/netbeans/modules/micronaut/newproject/FeaturesWizardPanel.java
@@ -0,0 +1,109 @@
+/*
+ * 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.newproject;
+
+import java.awt.Component;
+import javax.swing.event.ChangeListener;
+
+import org.openide.WizardDescriptor;
+import org.openide.WizardValidationException;
+import org.openide.util.ChangeSupport;
+import org.openide.util.HelpCtx;
+import org.openide.util.NbBundle;
+
+/**
+ * Wizard descriptor for Features step in Micronaut project wizard.
+ *
+ * @author Dusan Balek
+ */
+public class FeaturesWizardPanel implements WizardDescriptor.ValidatingPanel<WizardDescriptor>, WizardDescriptor.FinishablePanel<WizardDescriptor> {
+
+ WizardDescriptor wizardDescriptor;
+ private FeaturesVisual component;
+
+ public FeaturesWizardPanel() {
+ }
+
+ @Override
+ @NbBundle.Messages("LBL_Features=Features")
+ public Component getComponent() {
+ if (component == null) {
+ component = new FeaturesVisual(this);
+ component.setName(Bundle.LBL_Features());
+ }
+ return component;
+ }
+
+ @Override
+ public HelpCtx getHelp() {
+ // Show no Help button for this panel:
+ return HelpCtx.DEFAULT_HELP;
+ // If you have context help:
+ // return new HelpCtx("help.key.here");
+ }
+
+ @Override
+ public boolean isValid() {
+ getComponent();
+ return component.valid(wizardDescriptor);
+ }
+
+ private final ChangeSupport chgSupport = new ChangeSupport(this);
+
+ @Override
+ public final void addChangeListener(ChangeListener l) {
+ synchronized (chgSupport) {
+ chgSupport.addChangeListener(l);
+ }
+ }
+
+ @Override
+ public final void removeChangeListener(ChangeListener l) {
+ synchronized (chgSupport) {
+ chgSupport.removeChangeListener(l);
+ }
+ }
+
+ protected final void fireChangeEvent() {
+ chgSupport.fireChange();
+ }
+
+ @Override
+ public void readSettings(WizardDescriptor wiz) {
+ wizardDescriptor = wiz;
+ component.read(wizardDescriptor);
+ }
+
+ @Override
+ public void storeSettings(WizardDescriptor wiz) {
+ WizardDescriptor d = wiz;
+ component.store(d);
+ }
+
+ @Override
+ public boolean isFinishPanel() {
+ return false;
+ }
+
+ @Override
+ public void validate() throws WizardValidationException {
+ getComponent();
+ component.validate(wizardDescriptor);
+ }
+}
diff --git a/enterprise/micronaut/src/org/netbeans/modules/micronaut/newproject/MicronautGradleProjectDescription.html b/enterprise/micronaut/src/org/netbeans/modules/micronaut/newproject/MicronautGradleProjectDescription.html
new file mode 100644
index 0000000..7c2814e
--- /dev/null
+++ b/enterprise/micronaut/src/org/netbeans/modules/micronaut/newproject/MicronautGradleProjectDescription.html
@@ -0,0 +1,29 @@
+<!--
+
+ 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.
+
+-->
+<html>
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+ </head>
+ <body>
+ Gradle <b>Micronaut</b> project created trough the <i>Micronaut Launch</i> web service
+ (at <a href="https://micronaut.io/launch/">https://micronaut.io/launch/</a>)
+ </body>
+</html>
diff --git a/enterprise/micronaut/src/org/netbeans/modules/micronaut/newproject/MicronautLaunchService.java b/enterprise/micronaut/src/org/netbeans/modules/micronaut/newproject/MicronautLaunchService.java
new file mode 100644
index 0000000..6b318ed
--- /dev/null
+++ b/enterprise/micronaut/src/org/netbeans/modules/micronaut/newproject/MicronautLaunchService.java
@@ -0,0 +1,174 @@
+/*
+ * 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.newproject;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Singleton class managing connection to the Micronaut Launch service.
+ *
+ * @author Dusan Balek
+ */
+public class MicronautLaunchService {
+
+ public static final String defaultURL = "https://launch.micronaut.io";
+ public static final String snapshotURL = "https://snapshot.micronaut.io";
+
+ private static final MicronautLaunchService INSTANCE = new MicronautLaunchService();
+ private static final String VERSIONS = "versions/";
+ private static final String APPLICATION_TYPES = "application-types/";
+ private static final String FEATURES = "features/";
+ private static final String CREATE = "create/";
+
+ private final Gson gson = new Gson();
+
+ private MicronautLaunchService() {
+ }
+
+ public static MicronautLaunchService getInstance() {
+ return INSTANCE;
+ }
+
+ public String getMicronautVersion(String serviceUrl) throws IOException {
+ if (!serviceUrl.endsWith("/")) {
+ serviceUrl = serviceUrl + '/';
+ }
+ JsonObject json = gson.fromJson(getJson(serviceUrl + VERSIONS), JsonObject.class);
+ return json.getAsJsonObject("versions").get("micronaut.version").getAsString();
+ }
+
+ public List<ApplicationType> getApplicationTypes(String serviceUrl) throws IOException {
+ if (!serviceUrl.endsWith("/")) {
+ serviceUrl = serviceUrl + '/';
+ }
+ JsonObject json = gson.fromJson(getJson(serviceUrl + APPLICATION_TYPES), JsonObject.class);
+ JsonArray jsonArray = json.getAsJsonArray("types");
+ ArrayList<ApplicationType> types = new ArrayList<>(jsonArray.size());
+ for (JsonElement jsonElement : jsonArray) {
+ JsonObject jsonObject = jsonElement.getAsJsonObject();
+ types.add(new ApplicationType(jsonObject.get("title").getAsString(), jsonObject.get("value").getAsString()));
+ }
+ return types;
+ }
+
+ public List<Feature> getFeatures(String serviceUrl, ApplicationType appType) throws IOException {
+ if (!serviceUrl.endsWith("/")) {
+ serviceUrl = serviceUrl + '/';
+ }
+ JsonObject json = gson.fromJson(getJson(serviceUrl + APPLICATION_TYPES + appType.value + '/' + FEATURES), JsonObject.class);
+ JsonArray jsonArray = json.getAsJsonArray("features");
+ ArrayList<Feature> features = new ArrayList<>(jsonArray.size());
+ for (JsonElement jsonElement : jsonArray) {
+ JsonObject jsonObject = jsonElement.getAsJsonObject();
+ features.add(new Feature(jsonObject.get("title").getAsString(), jsonObject.get("name").getAsString(),
+ jsonObject.get("category").getAsString(), jsonObject.get("description").getAsString()));
+ }
+ return features;
+ }
+
+ public InputStream create(String serviceUrl, ApplicationType appType, String appName, String javaVersion, String language, String buildTool, Set<Feature> features) throws IOException {
+ StringBuilder sb = new StringBuilder(serviceUrl);
+ if (!serviceUrl.endsWith("/")) {
+ sb.append('/');
+ }
+ sb.append(CREATE).append(appType.value).append('/').append(appName);
+ sb.append("?javaVersion=JDK_").append(javaVersion);
+ sb.append("&lang=").append(language);
+ sb.append("&build=").append(buildTool);
+ sb.append("&test=JUNIT");
+ if (features != null) {
+ for (Feature feature : features) {
+ sb.append("&features=").append(feature.name);
+ }
+ }
+ return get(sb.toString());
+ }
+
+ private InputStream get(String serviceUrl) throws IOException {
+ URL url = new URL(serviceUrl);
+ HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+ conn.setRequestMethod("GET");
+ return conn.getInputStream();
+ }
+
+ private String getJson(String serviceUrl) throws IOException {
+ StringBuilder result = new StringBuilder();
+ try (BufferedReader rd = new BufferedReader(new InputStreamReader(get(serviceUrl)))) {
+ String line;
+ while ((line = rd.readLine()) != null) {
+ result.append(line);
+ }
+ }
+ return result.toString();
+ }
+
+ public static class ApplicationType {
+
+ private String title;
+ private String value;
+
+ private ApplicationType(String name, String value) {
+ this.title = name;
+ this.value = value;
+ }
+
+ @Override
+ public String toString() {
+ return title;
+ }
+ }
+
+ public static class Feature {
+ private String title;
+ private String name;
+ private String category;
+ private String description;
+
+ private Feature(String title, String name, String category, String description) {
+ this.title = title;
+ this.name = name;
+ this.category = category;
+ this.description = description;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public String getCategory() {
+ return category;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+ }
+}
diff --git a/enterprise/micronaut/src/org/netbeans/modules/micronaut/newproject/MicronautMavenProjectDescription.html b/enterprise/micronaut/src/org/netbeans/modules/micronaut/newproject/MicronautMavenProjectDescription.html
new file mode 100644
index 0000000..936484a
--- /dev/null
+++ b/enterprise/micronaut/src/org/netbeans/modules/micronaut/newproject/MicronautMavenProjectDescription.html
@@ -0,0 +1,29 @@
+<!--
+
+ 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.
+
+-->
+<html>
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+ </head>
+ <body>
+ Maven <b>Micronaut</b> project created trough the <i>Micronaut Launch</i> web service
+ (at <a href="https://micronaut.io/launch/">https://micronaut.io/launch/</a>)
+ </body>
+</html>
diff --git a/enterprise/micronaut/src/org/netbeans/modules/micronaut/newproject/MicronautProjectWizardIterator.java b/enterprise/micronaut/src/org/netbeans/modules/micronaut/newproject/MicronautProjectWizardIterator.java
new file mode 100644
index 0000000..3072d1c
--- /dev/null
+++ b/enterprise/micronaut/src/org/netbeans/modules/micronaut/newproject/MicronautProjectWizardIterator.java
@@ -0,0 +1,224 @@
+/*
+ * 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.newproject;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Collections;
+import java.util.NoSuchElementException;
+import java.util.Set;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+import javax.swing.JComponent;
+import javax.swing.event.ChangeListener;
+import org.netbeans.api.progress.ProgressHandle;
+import org.netbeans.api.project.Project;
+import org.netbeans.api.project.ProjectManager;
+import org.netbeans.api.templates.TemplateRegistration;
+import org.netbeans.spi.project.ActionProvider;
+import org.netbeans.spi.project.ui.support.ProjectChooser;
+import org.openide.WizardDescriptor;
+import org.openide.filesystems.FileObject;
+import org.openide.filesystems.FileUtil;
+import org.openide.util.AsyncGUIJob;
+import org.openide.util.Exceptions;
+import org.openide.util.Lookup;
+import org.openide.util.NbBundle;
+import org.openide.util.Utilities;
+
+/**
+ * Wizard iterator for Micronaut projects.
+ *
+ * @author Dusan Balek
+ */
+@NbBundle.Messages("MicronautProject_DN=Micronaut Project")
+public class MicronautProjectWizardIterator implements WizardDescriptor.ProgressInstantiatingIterator<WizardDescriptor> {
+
+ @TemplateRegistration(folder = "Project/Maven2", position = 300, displayName = "#MicronautProject_DN", description = "MicronautMavenProjectDescription.html", iconBase = "org/netbeans/modules/micronaut/resources/micronaut.png")
+ public static class MavenMicronautProject extends MicronautProjectWizardIterator {
+
+ @Override
+ public void initialize(WizardDescriptor wizard) {
+ super.initialize(wizard);
+ wizard.putProperty(BUILD_TOOL, "MAVEN");
+ }
+ }
+
+ @TemplateRegistration(folder = "Project/Gradle", position = 300, displayName = "#MicronautProject_DN", description = "MicronautGradleProjectDescription.html", iconBase = "org/netbeans/modules/micronaut/resources/micronaut.png")
+ public static class GradleMicronautProject extends MicronautProjectWizardIterator {
+
+ @Override
+ public void initialize(WizardDescriptor wizard) {
+ super.initialize(wizard);
+ wizard.putProperty(BUILD_TOOL, "GRADLE");
+ }
+ }
+
+ public static final String PROJECT_NAME = "project.name";
+ public static final String PROJECT_LOCATION = "project.location";
+ public static final String SERVICE_URL = "service.url";
+ public static final String MAVEN_GROUP = "maven.group";
+ public static final String MAVEN_ARTIFACT = "maven.artifact";
+ public static final String APPLICATION_TYPE = "application.type";
+ public static final String JAVA_VERSION = "java.version";
+ public static final String LANGUAGE = "language";
+ public static final String BUILD_TOOL = "build.tool";
+ public static final String FEATURES = "features";
+
+ private transient int index;
+ private transient WizardDescriptor.Panel[] panels;
+ private transient WizardDescriptor wiz;
+
+ @Override
+ public Set instantiate(ProgressHandle handle) throws IOException {
+ try {
+ handle.start(4);
+ File projFile = FileUtil.normalizeFile((File) wiz.getProperty(PROJECT_LOCATION));
+ projFile.mkdirs();
+ handle.progress(1);
+ InputStream stream = MicronautLaunchService.getInstance().create((String) wiz.getProperty(SERVICE_URL),
+ (MicronautLaunchService.ApplicationType) wiz.getProperty(APPLICATION_TYPE),
+ (String) wiz.getProperty(MAVEN_GROUP) + '.' + (String) wiz.getProperty(MAVEN_ARTIFACT),
+ (String) wiz.getProperty(JAVA_VERSION),
+ (String) wiz.getProperty(LANGUAGE),
+ (String) wiz.getProperty(BUILD_TOOL),
+ (Set<MicronautLaunchService.Feature>) wiz.getProperty(FEATURES));
+ handle.progress(2);
+ FileObject projDir = FileUtil.toFileObject(projFile);
+ unzip(stream, projDir);
+ handle.progress(3);
+ ProjectManager.getDefault().clearNonProjectCache();
+ Project prj = ProjectManager.getDefault().findProject(projDir);
+ if (prj != null) {
+ ActionProvider actionProvider = prj.getLookup().lookup(ActionProvider.class);
+ if (actionProvider != null && actionProvider.isActionEnabled(ActionProvider.COMMAND_PRIME, Lookup.EMPTY)) {
+ actionProvider.invokeAction(ActionProvider.COMMAND_PRIME, Lookup.EMPTY);
+ }
+ }
+ File parent = projFile.getParentFile();
+ if (parent != null && parent.exists()) {
+ ProjectChooser.setProjectsFolder(parent);
+ }
+ return Collections.singleton(projDir);
+ } catch (Exception ex) {
+ Exceptions.printStackTrace(ex);
+ } finally {
+ handle.finish();
+ }
+ return Collections.emptySet();
+ }
+
+ @Override
+ public Set instantiate() throws IOException {
+ throw new UnsupportedOperationException("Not supported.");
+ }
+
+ @Override
+ @NbBundle.Messages("LBL_WizardTitle=Micronaut Maven Project")
+ public void initialize(WizardDescriptor wizard) {
+ this.wiz = wizard;
+ wiz.putProperty("NewProjectWizard_Title", Bundle.LBL_WizardTitle()); //NOI18N
+ index = 0;
+ panels = new WizardDescriptor.Panel[] {
+ new NameAndLocationWizardPanel(),
+ new BasePropertiesWizardPanel(),
+ new FeaturesWizardPanel()
+ };
+ String[] steps = new String[] {
+ Bundle.LBL_NameAndLocationStep(),
+ Bundle.LBL_BasePropertiesStep(),
+ Bundle.LBL_Features()
+ };
+ for (int i = 0; i < panels.length; i++) {
+ JComponent jc = (JComponent) panels[i].getComponent();
+ jc.putClientProperty(WizardDescriptor.PROP_CONTENT_SELECTED_INDEX, i);
+ jc.putClientProperty(WizardDescriptor.PROP_CONTENT_DATA, steps);
+ }
+ Utilities.attachInitJob(panels[1].getComponent(), (AsyncGUIJob) panels[1].getComponent());
+ Utilities.attachInitJob(panels[2].getComponent(), (AsyncGUIJob) panels[2].getComponent());
+ }
+
+ @Override
+ public void uninitialize(WizardDescriptor wizard) {
+ }
+
+ @Override
+ public WizardDescriptor.Panel<WizardDescriptor> current() {
+ return panels[index];
+ }
+
+ @Override
+ @NbBundle.Messages({"# {0} - index", "# {1} - length", "NameFormat={0} of {1}"})
+ public String name() {
+ return Bundle.NameFormat(index + 1, panels.length);
+ }
+
+ @Override
+ public boolean hasNext() {
+ return index < panels.length - 1;
+ }
+
+ @Override
+ public boolean hasPrevious() {
+ return index > 0;
+ }
+
+ @Override
+ public void nextPanel() {
+ if (!hasNext()) {
+ throw new NoSuchElementException();
+ }
+ index++;
+ }
+
+ @Override
+ public void previousPanel() {
+ if (!hasPrevious()) {
+ throw new NoSuchElementException();
+ }
+ index--;
+ }
+
+ @Override
+ public void addChangeListener(ChangeListener l) {
+ }
+
+ @Override
+ public void removeChangeListener(ChangeListener l) {
+ }
+
+ private static void unzip(InputStream stream, FileObject folder) throws IOException {
+ try (ZipInputStream zis = new ZipInputStream(stream)) {
+ ZipEntry zipEntry;
+ while ((zipEntry = zis.getNextEntry()) != null) {
+ String entryName = zipEntry.getName();
+ if (zipEntry.isDirectory()) {
+ FileUtil.createFolder(folder, entryName);
+ } else {
+ FileObject fo = FileUtil.createData(folder, entryName);
+ try (OutputStream out = fo.getOutputStream()) {
+ FileUtil.copy(zis, out);
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/enterprise/micronaut/src/org/netbeans/modules/micronaut/newproject/NameAndLocationVisual.form b/enterprise/micronaut/src/org/netbeans/modules/micronaut/newproject/NameAndLocationVisual.form
new file mode 100644
index 0000000..adc9781
--- /dev/null
+++ b/enterprise/micronaut/src/org/netbeans/modules/micronaut/newproject/NameAndLocationVisual.form
@@ -0,0 +1,141 @@
+<?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.
+
+-->
+
+<Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
+ <AuxValues>
+ <AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
+ <AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
+ <AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
+ <AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
+ <AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
+ <AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
+ <AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
+ <AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
+ <AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
+ </AuxValues>
+
+ <Layout>
+ <DimensionLayout dim="0">
+ <Group type="103" groupAlignment="0" attributes="0">
+ <Group type="102" attributes="0">
+ <EmptySpace max="-2" attributes="0"/>
+ <Group type="103" groupAlignment="0" attributes="0">
+ <Component id="projectNameLabel" alignment="0" min="-2" max="-2" attributes="0"/>
+ <Component id="projectLocationLabel" alignment="0" min="-2" max="-2" attributes="0"/>
+ <Component id="createdFolderLabel" alignment="0" min="-2" max="-2" attributes="0"/>
+ </Group>
+ <EmptySpace min="-2" max="-2" attributes="0"/>
+ <Group type="103" groupAlignment="0" attributes="0">
+ <Component id="projectNameTextField" alignment="1" max="32767" attributes="0"/>
+ <Component id="projectLocationTextField" alignment="1" max="32767" attributes="0"/>
+ <Component id="createdFolderTextField" alignment="1" max="32767" attributes="0"/>
+ </Group>
+ <EmptySpace min="-2" max="-2" attributes="0"/>
+ <Component id="browseButton" min="-2" max="-2" attributes="0"/>
+ <EmptySpace max="-2" attributes="0"/>
+ </Group>
+ </Group>
+ </DimensionLayout>
+ <DimensionLayout dim="1">
+ <Group type="103" groupAlignment="0" attributes="0">
+ <Group type="102" attributes="0">
+ <EmptySpace max="-2" attributes="0"/>
+ <Group type="103" groupAlignment="3" attributes="0">
+ <Component id="projectNameLabel" alignment="3" min="-2" max="-2" attributes="0"/>
+ <Component id="projectNameTextField" alignment="3" min="-2" max="-2" attributes="0"/>
+ </Group>
+ <EmptySpace max="-2" attributes="0"/>
+ <Group type="103" groupAlignment="3" attributes="0">
+ <Component id="projectLocationLabel" alignment="3" min="-2" max="-2" attributes="0"/>
+ <Component id="projectLocationTextField" alignment="3" min="-2" max="-2" attributes="0"/>
+ <Component id="browseButton" alignment="3" min="-2" max="-2" attributes="0"/>
+ </Group>
+ <EmptySpace max="-2" attributes="0"/>
+ <Group type="103" groupAlignment="3" attributes="0">
+ <Component id="createdFolderLabel" alignment="3" min="-2" max="-2" attributes="0"/>
+ <Component id="createdFolderTextField" alignment="3" min="-2" max="-2" attributes="0"/>
+ </Group>
+ <EmptySpace max="32767" attributes="0"/>
+ </Group>
+ </Group>
+ </DimensionLayout>
+ </Layout>
+ <SubComponents>
+ <Component class="javax.swing.JLabel" name="projectNameLabel">
+ <Properties>
+ <Property name="labelFor" type="java.awt.Component" editor="org.netbeans.modules.form.ComponentChooserEditor">
+ <ComponentRef name="projectNameTextField"/>
+ </Property>
+ <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+ <ResourceString bundle="org/netbeans/modules/micronaut/newproject/Bundle.properties" key="LBL_ProjectName" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
+ </Property>
+ </Properties>
+ <AuxValues>
+ <AuxValue name="generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
+ </AuxValues>
+ </Component>
+ <Component class="javax.swing.JTextField" name="projectNameTextField">
+ </Component>
+ <Component class="javax.swing.JLabel" name="projectLocationLabel">
+ <Properties>
+ <Property name="labelFor" type="java.awt.Component" editor="org.netbeans.modules.form.ComponentChooserEditor">
+ <ComponentRef name="projectLocationTextField"/>
+ </Property>
+ <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+ <ResourceString bundle="org/netbeans/modules/micronaut/newproject/Bundle.properties" key="LBL_ProjectLocation" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
+ </Property>
+ </Properties>
+ <AuxValues>
+ <AuxValue name="generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
+ </AuxValues>
+ </Component>
+ <Component class="javax.swing.JTextField" name="projectLocationTextField">
+ </Component>
+ <Component class="javax.swing.JButton" name="browseButton">
+ <Properties>
+ <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+ <ResourceString bundle="org/netbeans/modules/micronaut/newproject/Bundle.properties" key="BTN_Browse" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
+ </Property>
+ <Property name="actionCommand" type="java.lang.String" value="BROWSE"/>
+ </Properties>
+ <Events>
+ <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="browseButtonActionPerformed"/>
+ </Events>
+ </Component>
+ <Component class="javax.swing.JLabel" name="createdFolderLabel">
+ <Properties>
+ <Property name="labelFor" type="java.awt.Component" editor="org.netbeans.modules.form.ComponentChooserEditor">
+ <ComponentRef name="createdFolderTextField"/>
+ </Property>
+ <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+ <ResourceString bundle="org/netbeans/modules/micronaut/newproject/Bundle.properties" key="LBL_ProjectFolder" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
+ </Property>
+ </Properties>
+ </Component>
+ <Component class="javax.swing.JTextField" name="createdFolderTextField">
+ <Properties>
+ <Property name="editable" type="boolean" value="false"/>
+ </Properties>
+ </Component>
+ </SubComponents>
+</Form>
diff --git a/enterprise/micronaut/src/org/netbeans/modules/micronaut/newproject/NameAndLocationVisual.java b/enterprise/micronaut/src/org/netbeans/modules/micronaut/newproject/NameAndLocationVisual.java
new file mode 100644
index 0000000..dc4d217
--- /dev/null
+++ b/enterprise/micronaut/src/org/netbeans/modules/micronaut/newproject/NameAndLocationVisual.java
@@ -0,0 +1,273 @@
+/*
+ * 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.newproject;
+
+import java.io.File;
+import javax.swing.JFileChooser;
+import javax.swing.JPanel;
+import javax.swing.event.DocumentEvent;
+import javax.swing.event.DocumentListener;
+import javax.swing.text.Document;
+import org.netbeans.spi.project.ui.support.ProjectChooser;
+import org.openide.WizardDescriptor;
+import org.openide.WizardValidationException;
+import org.openide.filesystems.FileUtil;
+import org.openide.util.NbBundle;
+
+/**
+ * Options panel for the Name and Location step in Micronaut project wizard.
+ *
+ * @author Dusan Balek
+ */
+public class NameAndLocationVisual extends JPanel implements DocumentListener {
+
+ public static final String PROP_PROJECT_NAME = "projectName";
+
+ private final NameAndLocationWizardPanel panel;
+
+ public NameAndLocationVisual(NameAndLocationWizardPanel panel) {
+ initComponents();
+ this.panel = panel;
+ // Register listener on the textFields to make the automatic updates
+ projectNameTextField.getDocument().addDocumentListener(this);
+ projectLocationTextField.getDocument().addDocumentListener(this);
+ }
+
+ /** This method is called from within the constructor to initialize the form. WARNING: Do NOT modify this code. The content of
+ * this method is always regenerated by the Form Editor.
+ */
+ // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
+ private void initComponents() {
+
+ projectNameLabel = new javax.swing.JLabel();
+ projectNameTextField = new javax.swing.JTextField();
+ projectLocationLabel = new javax.swing.JLabel();
+ projectLocationTextField = new javax.swing.JTextField();
+ browseButton = new javax.swing.JButton();
+ createdFolderLabel = new javax.swing.JLabel();
+ createdFolderTextField = new javax.swing.JTextField();
+
+ projectNameLabel.setLabelFor(projectNameTextField);
+ org.openide.awt.Mnemonics.setLocalizedText(projectNameLabel, org.openide.util.NbBundle.getMessage(NameAndLocationVisual.class, "LBL_ProjectName")); // NOI18N
+
+ projectLocationLabel.setLabelFor(projectLocationTextField);
+ org.openide.awt.Mnemonics.setLocalizedText(projectLocationLabel, org.openide.util.NbBundle.getMessage(NameAndLocationVisual.class, "LBL_ProjectLocation")); // NOI18N
+
+ org.openide.awt.Mnemonics.setLocalizedText(browseButton, org.openide.util.NbBundle.getMessage(NameAndLocationVisual.class, "BTN_Browse")); // NOI18N
+ browseButton.setActionCommand("BROWSE");
+ browseButton.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ browseButtonActionPerformed(evt);
+ }
+ });
+
+ createdFolderLabel.setLabelFor(createdFolderTextField);
+ org.openide.awt.Mnemonics.setLocalizedText(createdFolderLabel, org.openide.util.NbBundle.getMessage(NameAndLocationVisual.class, "LBL_ProjectFolder")); // NOI18N
+
+ createdFolderTextField.setEditable(false);
+
+ javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
+ this.setLayout(layout);
+ layout.setHorizontalGroup(
+ layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(layout.createSequentialGroup()
+ .addContainerGap()
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(projectNameLabel)
+ .addComponent(projectLocationLabel)
+ .addComponent(createdFolderLabel))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(projectNameTextField, javax.swing.GroupLayout.Alignment.TRAILING)
+ .addComponent(projectLocationTextField, javax.swing.GroupLayout.Alignment.TRAILING)
+ .addComponent(createdFolderTextField, javax.swing.GroupLayout.Alignment.TRAILING))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(browseButton)
+ .addContainerGap())
+ );
+ layout.setVerticalGroup(
+ layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(layout.createSequentialGroup()
+ .addContainerGap()
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(projectNameLabel)
+ .addComponent(projectNameTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(projectLocationLabel)
+ .addComponent(projectLocationTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(browseButton))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(createdFolderLabel)
+ .addComponent(createdFolderTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+ );
+ }// </editor-fold>//GEN-END:initComponents
+
+ @NbBundle.Messages("TIT_Select_Project_Location=Select Project Location")
+ private void browseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_browseButtonActionPerformed
+ String command = evt.getActionCommand();
+ if ("BROWSE".equals(command)) {
+ JFileChooser chooser = new JFileChooser();
+ chooser.setDialogTitle(Bundle.TIT_Select_Project_Location());
+ chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
+ String path = this.projectLocationTextField.getText();
+ if (path.length() > 0) {
+ File f = new File(path);
+ if (f.exists()) {
+ chooser.setSelectedFile(f);
+ }
+ }
+ if (JFileChooser.APPROVE_OPTION == chooser.showOpenDialog(this)) {
+ File projectDir = chooser.getSelectedFile();
+ projectLocationTextField.setText(FileUtil.normalizeFile(projectDir).getAbsolutePath());
+ }
+ panel.fireChangeEvent();
+ }
+ }//GEN-LAST:event_browseButtonActionPerformed
+
+ // Variables declaration - do not modify//GEN-BEGIN:variables
+ private javax.swing.JButton browseButton;
+ private javax.swing.JLabel createdFolderLabel;
+ private javax.swing.JTextField createdFolderTextField;
+ private javax.swing.JLabel projectLocationLabel;
+ private javax.swing.JTextField projectLocationTextField;
+ private javax.swing.JLabel projectNameLabel;
+ private javax.swing.JTextField projectNameTextField;
+ // End of variables declaration//GEN-END:variables
+
+ @Override
+ public void addNotify() {
+ super.addNotify();
+ //same problem as in 31086, initial focus on Cancel button
+ projectNameTextField.requestFocus();
+ }
+
+ boolean valid(WizardDescriptor wizardDescriptor) {
+ if (projectNameTextField.getText().length() == 0) {
+ wizardDescriptor.putProperty(WizardDescriptor.PROP_ERROR_MESSAGE, "Project Name is not a valid folder name.");
+ return false;
+ }
+ File f = FileUtil.normalizeFile(new File(projectLocationTextField.getText()).getAbsoluteFile());
+ if (!f.isDirectory()) {
+ wizardDescriptor.putProperty("WizardPanel_errorMessage", "Project Folder is not a valid path.");
+ return false;
+ }
+ final File destFolder = FileUtil.normalizeFile(new File(createdFolderTextField.getText()).getAbsoluteFile());
+ File projLoc = destFolder;
+ while (projLoc != null && !projLoc.exists()) {
+ projLoc = projLoc.getParentFile();
+ }
+ if (projLoc == null || !projLoc.canWrite()) {
+ wizardDescriptor.putProperty(WizardDescriptor.PROP_ERROR_MESSAGE, "Project Folder cannot be created.");
+ return false;
+ }
+ if (FileUtil.toFileObject(projLoc) == null) {
+ wizardDescriptor.putProperty(WizardDescriptor.PROP_ERROR_MESSAGE, "Project Folder is not a valid path.");
+ return false;
+ }
+ File[] kids = destFolder.listFiles();
+ if (destFolder.exists() && kids != null && kids.length > 0) {
+ wizardDescriptor.putProperty(WizardDescriptor.PROP_ERROR_MESSAGE, "Project Folder already exists and is not empty.");
+ return false;
+ }
+ wizardDescriptor.putProperty(WizardDescriptor.PROP_ERROR_MESSAGE, "");
+ return true;
+ }
+
+ void store(WizardDescriptor d) {
+ String name = projectNameTextField.getText().trim();
+ String folder = createdFolderTextField.getText().trim();
+ d.putProperty(MicronautProjectWizardIterator.PROJECT_NAME, name);
+ d.putProperty(MicronautProjectWizardIterator.PROJECT_LOCATION, new File(folder));
+ d.putProperty(MicronautProjectWizardIterator.MAVEN_ARTIFACT, toJavaName(name));
+ }
+
+ void read(WizardDescriptor settings) {
+ File projectLocation = (File) settings.getProperty(MicronautProjectWizardIterator.PROJECT_LOCATION);
+ if (projectLocation == null || projectLocation.getParentFile() == null || !projectLocation.getParentFile().isDirectory()) {
+ projectLocation = ProjectChooser.getProjectsFolder();
+ } else {
+ projectLocation = projectLocation.getParentFile();
+ }
+ this.projectLocationTextField.setText(projectLocation.getAbsolutePath());
+ String projectName = (String) settings.getProperty(MicronautProjectWizardIterator.PROJECT_NAME);
+ if (projectName == null) {
+ projectName = "MicronautProject";
+ }
+ this.projectNameTextField.setText(projectName);
+ this.projectNameTextField.selectAll();
+ }
+
+ void validate(WizardDescriptor d) throws WizardValidationException {
+ // nothing to validate
+ }
+
+ // Implementation of DocumentListener --------------------------------------
+ @Override
+ public void changedUpdate(DocumentEvent e) {
+ updateTexts(e);
+ if (this.projectNameTextField.getDocument() == e.getDocument()) {
+ firePropertyChange(PROP_PROJECT_NAME, null, this.projectNameTextField.getText());
+ }
+ }
+
+ @Override
+ public void insertUpdate(DocumentEvent e) {
+ updateTexts(e);
+ if (this.projectNameTextField.getDocument() == e.getDocument()) {
+ firePropertyChange(PROP_PROJECT_NAME, null, this.projectNameTextField.getText());
+ }
+ }
+
+ @Override
+ public void removeUpdate(DocumentEvent e) {
+ updateTexts(e);
+ if (this.projectNameTextField.getDocument() == e.getDocument()) {
+ firePropertyChange(PROP_PROJECT_NAME, null, this.projectNameTextField.getText());
+ }
+ }
+
+ /** Handles changes in the Project name and project directory, */
+ private void updateTexts(DocumentEvent e) {
+ Document doc = e.getDocument();
+ if (doc == projectNameTextField.getDocument() || doc == projectLocationTextField.getDocument()) {
+ // Change in the project name
+ String projectName = projectNameTextField.getText();
+ String projectFolder = projectLocationTextField.getText();
+ createdFolderTextField.setText(projectFolder + File.separatorChar + projectName);
+ }
+ // Notify that the panel changed
+ panel.fireChangeEvent();
+ }
+
+ private static String toJavaName(String name) {
+ StringBuilder javaName = new StringBuilder(name);
+ if (!Character.isJavaIdentifierStart(javaName.charAt(0))) {
+ javaName.setCharAt(0, '_');
+ }
+ for (int i = 1; i < name.length(); i++) {
+ if (!Character.isJavaIdentifierPart(javaName.charAt(i))) {
+ javaName.setCharAt(i, '_');
+ }
+ }
+ return javaName.toString();
+ }
+}
diff --git a/enterprise/micronaut/src/org/netbeans/modules/micronaut/newproject/NameAndLocationWizardPanel.java b/enterprise/micronaut/src/org/netbeans/modules/micronaut/newproject/NameAndLocationWizardPanel.java
new file mode 100644
index 0000000..4760ee6
--- /dev/null
+++ b/enterprise/micronaut/src/org/netbeans/modules/micronaut/newproject/NameAndLocationWizardPanel.java
@@ -0,0 +1,108 @@
+/*
+ * 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.newproject;
+
+import java.awt.Component;
+import javax.swing.event.ChangeListener;
+import org.openide.WizardDescriptor;
+import org.openide.WizardValidationException;
+import org.openide.util.ChangeSupport;
+import org.openide.util.HelpCtx;
+import org.openide.util.NbBundle;
+
+/**
+ * Wizard descriptor for Name and Location step in Micronaut project wizard.
+ *
+ * @author Dusan Balek
+ */
+public class NameAndLocationWizardPanel implements WizardDescriptor.ValidatingPanel<WizardDescriptor>, WizardDescriptor.FinishablePanel<WizardDescriptor> {
+
+ private WizardDescriptor wizardDescriptor;
+ private NameAndLocationVisual component;
+
+ public NameAndLocationWizardPanel() {
+ }
+
+ @Override
+ @NbBundle.Messages("LBL_NameAndLocationStep=Name and Location")
+ public Component getComponent() {
+ if (component == null) {
+ component = new NameAndLocationVisual(this);
+ component.setName(Bundle.LBL_NameAndLocationStep());
+ }
+ return component;
+ }
+
+ @Override
+ public HelpCtx getHelp() {
+ // Show no Help button for this panel:
+ return HelpCtx.DEFAULT_HELP;
+ // If you have context help:
+ // return new HelpCtx("help.key.here");
+ }
+
+ @Override
+ public boolean isValid() {
+ getComponent();
+ return component.valid(wizardDescriptor);
+ }
+
+ private final ChangeSupport chgSupport = new ChangeSupport(this);
+
+ @Override
+ public final void addChangeListener(ChangeListener l) {
+ synchronized (chgSupport) {
+ chgSupport.addChangeListener(l);
+ }
+ }
+
+ @Override
+ public final void removeChangeListener(ChangeListener l) {
+ synchronized (chgSupport) {
+ chgSupport.removeChangeListener(l);
+ }
+ }
+
+ protected final void fireChangeEvent() {
+ chgSupport.fireChange();
+ }
+
+ @Override
+ public void readSettings(WizardDescriptor wiz) {
+ wizardDescriptor = wiz;
+ component.read(wizardDescriptor);
+ }
+
+ @Override
+ public void storeSettings(WizardDescriptor wiz) {
+ WizardDescriptor d = wiz;
+ component.store(d);
+ }
+
+ @Override
+ public boolean isFinishPanel() {
+ return false;
+ }
+
+ @Override
+ public void validate() throws WizardValidationException {
+ getComponent();
+ component.validate(wizardDescriptor);
+ }
+}
diff --git a/enterprise/micronaut/src/org/netbeans/modules/micronaut/refactor/MicronautRefactoringFactory.java b/enterprise/micronaut/src/org/netbeans/modules/micronaut/refactor/MicronautRefactoringFactory.java
new file mode 100644
index 0000000..f2e1ef5
--- /dev/null
+++ b/enterprise/micronaut/src/org/netbeans/modules/micronaut/refactor/MicronautRefactoringFactory.java
@@ -0,0 +1,243 @@
+/*
+ * 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.refactor;
+
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.Set;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.TypeElement;
+import javax.swing.text.Position;
+import org.netbeans.api.java.project.JavaProjectConstants;
+import org.netbeans.api.java.source.JavaSource;
+import org.netbeans.api.java.source.TreePathHandle;
+import org.netbeans.api.project.FileOwnerQuery;
+import org.netbeans.api.project.Project;
+import org.netbeans.api.project.ProjectUtils;
+import org.netbeans.api.project.SourceGroup;
+import org.netbeans.modules.micronaut.MicronautConfigProperties;
+import org.netbeans.modules.micronaut.MicronautConfigUtilities;
+import org.netbeans.modules.micronaut.completion.MicronautConfigCompletionItem;
+import org.netbeans.modules.refactoring.api.AbstractRefactoring;
+import org.netbeans.modules.refactoring.api.Problem;
+import org.netbeans.modules.refactoring.api.Scope;
+import org.netbeans.modules.refactoring.api.WhereUsedQuery;
+import org.netbeans.modules.refactoring.spi.RefactoringElementsBag;
+import org.netbeans.modules.refactoring.spi.RefactoringPlugin;
+import org.netbeans.modules.refactoring.spi.RefactoringPluginFactory;
+import org.netbeans.modules.refactoring.spi.SimpleRefactoringElementImplementation;
+import org.openide.cookies.EditorCookie;
+import org.openide.filesystems.FileObject;
+import org.openide.loaders.DataObject;
+import org.openide.loaders.DataObjectNotFoundException;
+import org.openide.text.CloneableEditorSupport;
+import org.openide.text.PositionBounds;
+import org.openide.util.Exceptions;
+import org.openide.util.Lookup;
+import org.openide.util.lookup.ServiceProvider;
+import org.springframework.boot.configurationmetadata.ConfigurationMetadataGroup;
+import org.springframework.boot.configurationmetadata.ConfigurationMetadataProperty;
+import org.springframework.boot.configurationmetadata.ConfigurationMetadataSource;
+
+/**
+ *
+ * @author Dusan Balek
+ */
+@ServiceProvider(service=org.netbeans.modules.refactoring.spi.RefactoringPluginFactory.class)
+public class MicronautRefactoringFactory implements RefactoringPluginFactory {
+
+ @Override
+ public RefactoringPlugin createInstance(AbstractRefactoring refactoring) {
+ Lookup lkp = refactoring.getRefactoringSource();
+ TreePathHandle handle = lkp.lookup(TreePathHandle.class);
+ if (refactoring instanceof WhereUsedQuery && ((WhereUsedQuery) refactoring).getBooleanValue(WhereUsedQuery.FIND_REFERENCES)) {
+ if (handle != null) {
+ return new MicronautWhereUsedRefactoringPlugin((WhereUsedQuery) refactoring);
+ }
+ }
+ return null;
+ }
+
+ private static class MicronautWhereUsedRefactoringPlugin implements RefactoringPlugin {
+
+ private final WhereUsedQuery refactoring;
+
+ public MicronautWhereUsedRefactoringPlugin(WhereUsedQuery refactoring) {
+ this.refactoring = refactoring;
+ }
+
+ @Override
+ public Problem preCheck() {
+ return null;
+ }
+
+ @Override
+ public Problem checkParameters() {
+ return null;
+ }
+
+ @Override
+ public Problem fastCheckParameters() {
+ return null;
+ }
+
+ @Override
+ public void cancelRequest() {
+ }
+
+ @Override
+ public Problem prepare(RefactoringElementsBag refactoringElements) {
+ Scope scope = refactoring.getContext().lookup(Scope.class);
+ if (scope != null) {
+ Set<Project> projects = new HashSet<>();
+ for (FileObject sourceRoot : scope.getSourceRoots()) {
+ Project p = FileOwnerQuery.getOwner(sourceRoot);
+ if (p != null) {
+ projects.add(p);
+ }
+ }
+ try {
+ Info info = null;
+ for (Project project : projects) {
+ if (MicronautConfigProperties.hasConfigMetadata(project)) {
+ SourceGroup[] sourceGroups = ProjectUtils.getSources(project).getSourceGroups(JavaProjectConstants.SOURCES_TYPE_RESOURCES);
+ for (SourceGroup sourceGroup : sourceGroups) {
+ FileObject rootFolder = sourceGroup.getRootFolder();
+ FileObject fo = rootFolder.getFileObject("application.yml");
+ if (fo != null) {
+ if (info == null) {
+ info = getInfo();
+ }
+ if (info.className != null && info.methodName != null && info.methodName.startsWith("set")) {
+ for (ConfigurationMetadataGroup group : MicronautConfigProperties.getGroups(project).values()) {
+ ConfigurationMetadataSource source = group.getSources().get(info.className);
+ if (source != null) {
+ for (ConfigurationMetadataProperty property : source.getProperties().values()) {
+ String name = "set" + property.getName().replaceAll("-", "");
+ if (name.equalsIgnoreCase(info.methodName)) {
+ MicronautConfigUtilities.collectUsages(fo, property.getId(), usage -> {
+ refactoringElements.add(refactoring, new WhereUsedRefactoringElement(usage.getFileObject(), usage.getStartOffset(), usage.getEndOffset(), usage.getText()));
+ });
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ } catch (IOException ioe) {
+ }
+ }
+ return null;
+ }
+
+ private Info getInfo() throws IOException {
+ final TreePathHandle handle = refactoring.getRefactoringSource().lookup(TreePathHandle.class);
+ final Info info = new Info();
+ JavaSource source = JavaSource.forFileObject(handle.getFileObject());
+ if (source != null) {
+ source.runUserActionTask(controller -> {
+ controller.toPhase(JavaSource.Phase.RESOLVED);
+ Element el = handle.resolveElement(controller);
+ if (el != null && el.getKind() == ElementKind.METHOD && el.getModifiers().contains(Modifier.PUBLIC)) {
+ info.methodName = el.getSimpleName().toString();
+ info.className = ((TypeElement)((ExecutableElement)el).getEnclosingElement()).getQualifiedName().toString();
+ }
+ }, true);
+ }
+ return info;
+ }
+
+ private static class Info {
+ private String className;
+ private String methodName;
+ }
+ }
+
+ public static final class WhereUsedRefactoringElement extends SimpleRefactoringElementImplementation {
+
+ private final FileObject fileObject;
+ private final int startOffset;
+ private final int endOffset;
+ private final String text;
+
+ private WhereUsedRefactoringElement(FileObject fileObject, int startOffset, int endOffset, String text) {
+ this.fileObject = fileObject;
+ this.startOffset = startOffset;
+ this.endOffset = endOffset;
+ this.text = text;
+ }
+
+ @Override
+ public String getText() {
+ return text;
+ }
+
+ @Override
+ public String getDisplayText() {
+ StringBuilder sb = new StringBuilder();
+ int idx = text.indexOf(':');
+ if (idx < 0) {
+ sb.append(text);
+ } else {
+ sb.append(MicronautConfigCompletionItem.PROPERTY_NAME_COLOR).append("<b>");
+ sb.append(text.substring(0, idx));
+ sb.append("</b></font>");
+ sb.append(text.substring(idx));
+ }
+ return sb.toString();
+ }
+
+ @Override
+ public void performChange() {
+ }
+
+ @Override
+ public Lookup getLookup() {
+ return Lookup.EMPTY;
+ }
+
+ @Override
+ public FileObject getParentFile() {
+ return fileObject;
+ }
+
+ @Override
+ public PositionBounds getPosition() {
+ try {
+ DataObject dobj = DataObject.find(getParentFile());
+ if (dobj != null) {
+ EditorCookie.Observable obs = (EditorCookie.Observable)dobj.getCookie(EditorCookie.Observable.class);
+ if (obs != null && obs instanceof CloneableEditorSupport) {
+ CloneableEditorSupport supp = (CloneableEditorSupport)obs;
+ return new PositionBounds(supp.createPositionRef(startOffset, Position.Bias.Forward), supp.createPositionRef(Math.max(startOffset, endOffset), Position.Bias.Forward));
+ }
+ }
+ } catch (DataObjectNotFoundException ex) {
+ Exceptions.printStackTrace(ex);
+ }
+ return null;
+ }
+ }
+}
diff --git a/enterprise/micronaut/src/org/netbeans/modules/micronaut/resources/layer.xml b/enterprise/micronaut/src/org/netbeans/modules/micronaut/resources/layer.xml
new file mode 100644
index 0000000..db3eff9
--- /dev/null
+++ b/enterprise/micronaut/src/org/netbeans/modules/micronaut/resources/layer.xml
@@ -0,0 +1,33 @@
+<?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 filesystem PUBLIC "-//NetBeans//DTD Filesystem 1.2//EN" "http://www.netbeans.org/dtds/filesystem-1_2.dtd">
+<filesystem>
+ <folder name="Editors">
+ <folder name="text">
+ <folder name="x-java">
+ <folder name="AnnotationProcessors">
+ <file name="org-netbeans-modules-micronaut-completion-MicronautJavaConfigPropertiesCompletion.instance"/>
+ </folder>
+ </folder>
+ </folder>
+ </folder>
+</filesystem>
diff --git a/enterprise/micronaut/src/org/netbeans/modules/micronaut/resources/micronaut.png b/enterprise/micronaut/src/org/netbeans/modules/micronaut/resources/micronaut.png
new file mode 100644
index 0000000..d38f73f
Binary files /dev/null and b/enterprise/micronaut/src/org/netbeans/modules/micronaut/resources/micronaut.png differ
diff --git a/ide/api.lsp/build.xml b/ide/api.lsp/build.xml
new file mode 100644
index 0000000..9b6a11d
--- /dev/null
+++ b/ide/api.lsp/build.xml
@@ -0,0 +1,25 @@
+<?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.
+
+-->
+<project basedir="." default="netbeans" name="ide/api.lsp">
+ <description>Builds, tests, and runs the project org.netbeans.api.lsp</description>
+ <import file="../../nbbuild/templates/projectized.xml"/>
+</project>
diff --git a/ide/api.lsp/manifest.mf b/ide/api.lsp/manifest.mf
new file mode 100644
index 0000000..2a6458d
--- /dev/null
+++ b/ide/api.lsp/manifest.mf
@@ -0,0 +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.0
+AutoUpdate-Show-In-Client: false
+
diff --git a/ide/libs.bytelist/external/binaries-list b/ide/api.lsp/nbproject/project.properties
similarity index 83%
copy from ide/libs.bytelist/external/binaries-list
copy to ide/api.lsp/nbproject/project.properties
index 0e0b70c1..0f73a11 100644
--- a/ide/libs.bytelist/external/binaries-list
+++ b/ide/api.lsp/nbproject/project.properties
@@ -14,5 +14,6 @@
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
-80294E59315B314D57782DC37F983AE5B29C2E4C org.jruby.extras:bytelist:1.0.15
-E2C76A19F00128BB1806207E2989139BFB45F49D org.jruby.jcodings:jcodings:1.0.18
+is.autoload=true
+javac.source=1.8
+javac.compilerargs=-Xlint -Xlint:-serial
diff --git a/ide/api.lsp/nbproject/project.xml b/ide/api.lsp/nbproject/project.xml
new file mode 100644
index 0000000..3d8cea7
--- /dev/null
+++ b/ide/api.lsp/nbproject/project.xml
@@ -0,0 +1,105 @@
+<?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.
+
+-->
+<project xmlns="http://www.netbeans.org/ns/project/1">
+ <type>org.netbeans.modules.apisupport.project</type>
+ <configuration>
+ <data xmlns="http://www.netbeans.org/ns/nb-module-project/3">
+ <code-name-base>org.netbeans.api.lsp</code-name-base>
+ <module-dependencies>
+ <dependency>
+ <code-name-base>org.netbeans.api.annotations.common</code-name-base>
+ <build-prerequisite/>
+ <compile-dependency/>
+ <run-dependency>
+ <release-version>1</release-version>
+ <specification-version>1.39</specification-version>
+ </run-dependency>
+ </dependency>
+ <dependency>
+ <code-name-base>org.netbeans.modules.editor.mimelookup</code-name-base>
+ <build-prerequisite/>
+ <compile-dependency/>
+ <run-dependency>
+ <release-version>1</release-version>
+ <specification-version>1.52</specification-version>
+ </run-dependency>
+ </dependency>
+ <dependency>
+ <code-name-base>org.netbeans.modules.editor.util</code-name-base>
+ <build-prerequisite/>
+ <compile-dependency/>
+ <run-dependency>
+ <release-version>1</release-version>
+ <specification-version>1.77</specification-version>
+ </run-dependency>
+ </dependency>
+ <dependency>
+ <code-name-base>org.openide.filesystems</code-name-base>
+ <build-prerequisite/>
+ <compile-dependency/>
+ <run-dependency>
+ <specification-version>9.22</specification-version>
+ </run-dependency>
+ </dependency>
+ <dependency>
+ <code-name-base>org.openide.util</code-name-base>
+ <build-prerequisite/>
+ <compile-dependency/>
+ <run-dependency>
+ <specification-version>9.19</specification-version>
+ </run-dependency>
+ </dependency>
+ <dependency>
+ <code-name-base>org.openide.util.lookup</code-name-base>
+ <build-prerequisite/>
+ <compile-dependency/>
+ <run-dependency>
+ <specification-version>8.46</specification-version>
+ </run-dependency>
+ </dependency>
+ </module-dependencies>
+ <test-dependencies>
+ <test-type>
+ <name>unit</name>
+ <test-dependency>
+ <code-name-base>org.netbeans.libs.junit4</code-name-base>
+ <compile-dependency/>
+ </test-dependency>
+ <test-dependency>
+ <code-name-base>org.netbeans.modules.editor.mimelookup</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-type>
+ </test-dependencies>
+ <public-packages>
+ <package>org.netbeans.api.lsp</package>
+ <package>org.netbeans.spi.lsp</package>
+ </public-packages>
+ </data>
+ </configuration>
+</project>
diff --git a/ide/libs.bytelist/external/binaries-list b/ide/api.lsp/src/org/netbeans/api/lsp/Bundle.properties
similarity index 76%
copy from ide/libs.bytelist/external/binaries-list
copy to ide/api.lsp/src/org/netbeans/api/lsp/Bundle.properties
index 0e0b70c1..a610422 100644
--- a/ide/libs.bytelist/external/binaries-list
+++ b/ide/api.lsp/src/org/netbeans/api/lsp/Bundle.properties
@@ -14,5 +14,8 @@
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
-80294E59315B314D57782DC37F983AE5B29C2E4C org.jruby.extras:bytelist:1.0.15
-E2C76A19F00128BB1806207E2989139BFB45F49D org.jruby.jcodings:jcodings:1.0.18
+OpenIDE-Module-Display-Category=Base IDE
+OpenIDE-Module-Long-Description=\
+ Provides APIs and support classes for Language Server implementations.
+OpenIDE-Module-Name=LSP APIs
+OpenIDE-Module-Short-Description=Language Server Protocol APIs
diff --git a/ide/api.lsp/src/org/netbeans/api/lsp/Completion.java b/ide/api.lsp/src/org/netbeans/api/lsp/Completion.java
new file mode 100644
index 0000000..797714e
--- /dev/null
+++ b/ide/api.lsp/src/org/netbeans/api/lsp/Completion.java
@@ -0,0 +1,392 @@
+/*
+ * 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.Collections;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import java.util.function.Consumer;
+import javax.swing.text.Document;
+import org.netbeans.api.annotations.common.CheckForNull;
+import org.netbeans.api.annotations.common.NonNull;
+import org.netbeans.api.annotations.common.NullAllowed;
+import org.netbeans.api.editor.mimelookup.MimeLookup;
+import org.netbeans.api.editor.mimelookup.MimePath;
+import org.netbeans.lib.editor.util.swing.DocumentUtilities;
+import org.netbeans.modules.lsp.CompletionAccessor;
+import org.netbeans.spi.lsp.CompletionCollector;
+
+/**
+ * Represents a completion proposal.
+ *
+ * @author Dusan Balek
+ * @since 1.0
+ */
+public final class Completion {
+
+ static {
+ CompletionAccessor.setDefault(new CompletionAccessor() {
+ @Override
+ public Completion createCompletion(String label, Kind kind, List<Tag> tags, CompletableFuture<String> detail, CompletableFuture<String> documentation,
+ boolean preselect, String sortText, String filterText, String insertText, TextFormat insertTextFormat, TextEdit textEdit, CompletableFuture<List<TextEdit>> additionalTextEdits,
+ List<Character> commitCharacters) {
+ return new Completion(label, kind, tags, detail, documentation, preselect, sortText, filterText, insertText, insertTextFormat, textEdit, additionalTextEdits, commitCharacters);
+ }
+ });
+ }
+
+ private final String label;
+ private final Kind kind;
+ private final List<Tag> tags;
+ private final CompletableFuture<String> detail;
+ private final CompletableFuture<String> documentation;
+ private final boolean preselect;
+ private final String sortText;
+ private final String filterText;
+ private final String insertText;
+ private final TextFormat insertTextFormat;
+ private final TextEdit textEdit;
+ private final CompletableFuture<List<TextEdit>> additionalTextEdits;
+ private final List<Character> commitCharacters;
+
+ private Completion(String label, Kind kind, List<Tag> tags, CompletableFuture<String> detail, CompletableFuture<String> documentation,
+ boolean preselect, String sortText, String filterText, String insertText, TextFormat insertTextFormat,
+ TextEdit textEdit, CompletableFuture<List<TextEdit>> additionalTextEdits, List<Character> commitCharacters) {
+ this.label = label;
+ this.kind = kind;
+ this.tags = tags;
+ this.detail = detail;
+ this.documentation = documentation;
+ this.preselect = preselect;
+ this.sortText = sortText;
+ this.filterText = filterText;
+ this.insertText = insertText;
+ this.insertTextFormat = insertTextFormat;
+ this.textEdit = textEdit;
+ this.additionalTextEdits = additionalTextEdits;
+ this.commitCharacters = commitCharacters;
+ }
+
+ /**
+ * The label of this completion. By default also the text that is inserted
+ * when selecting this completion.
+ *
+ * @since 1.0
+ */
+ @NonNull
+ public String getLabel() {
+ return label;
+ }
+
+ /**
+ * The kind of this completion.
+ *
+ * @since 1.0
+ */
+ @CheckForNull
+ public Kind getKind() {
+ return kind;
+ }
+
+ /**
+ * Tags for this completion.
+ *
+ * @since 1.0
+ */
+ @CheckForNull
+ public List<Tag> getTags() {
+ return tags != null ? Collections.unmodifiableList(tags) : null;
+ }
+
+ /**
+ * A human-readable string with additional information
+ * about this completion, like type or symbol information.
+ *
+ * @since 1.0
+ */
+ @CheckForNull
+ public CompletableFuture<String> getDetail() {
+ return detail;
+ }
+
+ /**
+ * A human-readable string that represents a doc-comment. An HTML format is
+ * supported.
+ *
+ * @since 1.0
+ */
+ @CheckForNull
+ public CompletableFuture<String> getDocumentation() {
+ return documentation;
+ }
+
+ /**
+ * Select this completion when showing.
+ *
+ * @since 1.0
+ */
+ public boolean isPreselect() {
+ return preselect;
+ }
+
+ /**
+ * A string that should be used when comparing this completion with other
+ * completions. When {@code null} the label is used as the sort text.
+ *
+ * @since 1.0
+ */
+ @CheckForNull
+ public String getSortText() {
+ return sortText;
+ }
+
+ /**
+ * A string that should be used when filtering a set of completions.
+ * When {@code null} the label is used as the filter.
+ *
+ * @since 1.0
+ */
+ @CheckForNull
+ public String getFilterText() {
+ return filterText;
+ }
+
+ /**
+ * A string that should be inserted into a document when selecting
+ * this completion. When {@code null} the label is used as the insert text.
+ *
+ * @since 1.0
+ */
+ @CheckForNull
+ public String getInsertText() {
+ return insertText;
+ }
+
+ /**
+ * The format of the insert text. The format applies to both the
+ * {@code insertText} property and the {@code newText} property of a provided
+ * {@code textEdit}. If omitted defaults to {@link TextFormat#PlainText}.
+ *
+ * @since 1.0
+ */
+ @CheckForNull
+ public TextFormat getInsertTextFormat() {
+ return insertTextFormat;
+ }
+
+ /**
+ * An edit which is applied to a document when selecting this completion.
+ * When an edit is provided the value of {@code insertText} is ignored.
+ * The range of the edit must be a single line range and it must
+ * contain the position at which completion has been requested.
+ *
+ * @since 1.0
+ */
+ @CheckForNull
+ public TextEdit getTextEdit() {
+ return textEdit;
+ }
+
+ /**
+ * A list of additional text edits that are applied when selecting this
+ * completion. Edits must not overlap (including the same insert position)
+ * with the main edit nor with themselves.
+ * Additional text edits should be used to change text unrelated to the
+ * current cursor position (for example adding an import statement at the
+ * top of the file if the completion item will insert an unqualified type).
+ *
+ * @since 1.0
+ */
+ @CheckForNull
+ public CompletableFuture<List<TextEdit>> getAdditionalTextEdits() {
+ return additionalTextEdits;
+ }
+
+ /**
+ * A list of characters that when pressed while this completion is
+ * active will accept it first and then type that character.
+ *
+ * @since 1.0
+ */
+ @CheckForNull
+ public List<Character> getCommitCharacters() {
+ return commitCharacters != null ? Collections.unmodifiableList(commitCharacters) : null;
+ }
+
+ /**
+ * Computes and collects completions for a document at a given offset. Example
+ * usage can be illustrated by:
+ * {@codesnippet CompletionTest#testCompletionCollect}
+ *
+ * @param doc a text document
+ * @param offset an offset inside the text document
+ * @param context an optional completion context
+ * @param consumer an operation accepting collected completions
+ *
+ * @return true if the list of collected completion is complete. If {@code false},
+ * further typing should result in subsequent calls to this method to recompute
+ * the completions.
+ *
+ * @since 1.0
+ */
+ public static boolean collect(@NonNull Document doc, int offset, @NullAllowed Context context, @NonNull Consumer<Completion> consumer) {
+ boolean isComplete = true;
+ MimePath mimePath = MimePath.parse(DocumentUtilities.getMimeType(doc));
+ for (CompletionCollector collector : MimeLookup.getLookup(mimePath).lookupAll(CompletionCollector.class)) {
+ isComplete &= collector.collectCompletions(doc, offset, context, consumer);
+ }
+ return isComplete;
+ }
+
+ /**
+ * Contains additional information about the context in which a request for
+ * collections completions is triggered.
+ *
+ * @since 1.0
+ */
+ public static final class Context {
+
+ private final TriggerKind triggerKind;
+ private final Character triggerCharacter;
+
+ public Context(@NonNull TriggerKind triggerKind, @NullAllowed Character triggerCharacter) {
+ this.triggerKind = triggerKind;
+ this.triggerCharacter = triggerCharacter;
+ }
+
+ /**
+ * How the completion was triggered.
+ *
+ * @since 1.0
+ */
+ @NonNull
+ public TriggerKind getTriggerKind() {
+ return triggerKind;
+ }
+
+ /**
+ * The trigger character that has trigger code complete.
+ * Is undefined if {@code triggerKind != TriggerKind.TriggerCharacter}.
+ *
+ * @since 1.0
+ */
+ @CheckForNull
+ public Character getTriggerCharacter() {
+ return triggerCharacter;
+ }
+ }
+
+ /**
+ * Specifies how a completion was triggered.
+ *
+ * @since 1.0
+ */
+ public enum TriggerKind {
+
+ /**
+ * Completion was triggered by typing an identifier (24x7 code
+ * complete), manual invocation (e.g Ctrl+Space) or via API.
+ *
+ * @since 1.0
+ */
+ Invoked,
+
+ /**
+ * Completion was triggered by a trigger character.
+ *
+ * @since 1.0
+ */
+ TriggerCharacter,
+
+ /**
+ * Completion was re-triggered as the current completion list is incomplete.
+ *
+ * @since 1.0
+ */
+ TriggerForIncompleteCompletions
+ }
+
+ /**
+ * The kind of a completion.
+ *
+ * @since 1.0
+ */
+ public static enum Kind {
+
+ Text,
+ Method,
+ Function,
+ Constructor,
+ Field,
+ Variable,
+ Class,
+ Interface,
+ Module,
+ Property,
+ Unit,
+ Value,
+ Enum,
+ Keyword,
+ Snippet,
+ Color,
+ File,
+ Reference,
+ Folder,
+ EnumMember,
+ Constant,
+ Struct,
+ Event,
+ Operator,
+ TypeParameter
+ }
+
+ /**
+ * Completion item tags are extra annotations that tweak the rendering of a
+ * completion.
+ *
+ * @since 1.0
+ */
+ public static enum Tag {
+
+ Deprecated
+ }
+
+ /**
+ * Defines whether the insert text in a completion item should be interpreted
+ * as plain text or a snippet.
+ *
+ * @since 1.0
+ */
+ public static enum TextFormat {
+
+ /**
+ * The primary text to be inserted is treated as a plain string.
+ *
+ * @since 1.0
+ */
+ PlainText,
+
+ /**
+ * The primary text to be inserted is treated as a snippet.
+ *
+ * @since 1.0
+ */
+ Snippet
+ }
+}
diff --git a/ide/api.lsp/src/org/netbeans/api/lsp/HyperlinkLocation.java b/ide/api.lsp/src/org/netbeans/api/lsp/HyperlinkLocation.java
new file mode 100644
index 0000000..1c449a5
--- /dev/null
+++ b/ide/api.lsp/src/org/netbeans/api/lsp/HyperlinkLocation.java
@@ -0,0 +1,160 @@
+/*
+ * 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.ArrayList;
+import java.util.List;
+import java.util.Objects;
+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.modules.lsp.HyperlinkLocationAccessor;
+import org.netbeans.spi.lsp.HyperlinkLocationProvider;
+import org.openide.filesystems.FileObject;
+
+/**
+ * Represents the target location of a hyperlink. Location is a range inside a
+ * file object, such as a line inside a text file.
+ *
+ * @author Dusan Balek
+ * @since 1.0
+ */
+public final class HyperlinkLocation {
+
+ static {
+ HyperlinkLocationAccessor.setDefault(new HyperlinkLocationAccessor() {
+ @Override
+ public HyperlinkLocation createHyperlinkLocation(FileObject fileObject, int startOffset, int endOffset) {
+ return new HyperlinkLocation(fileObject, startOffset, endOffset);
+ }
+ });
+ }
+
+ private final FileObject fileObject;
+ private final int startOffset;
+ private final int endOffset;
+
+ private HyperlinkLocation(@NonNull FileObject fileObject, int startOffset, int endOffset) {
+ this.fileObject = fileObject;
+ this.startOffset = startOffset;
+ this.endOffset = endOffset;
+ }
+
+ /**
+ * Target file object of this hyperlink.
+ *
+ * @return file object
+ *
+ * @since 1.0
+ */
+ @NonNull
+ public FileObject getFileObject() {
+ return fileObject;
+ }
+
+ /**
+ * The start offset of a hyperlink's target range.
+ *
+ * @return offset
+ *
+ * @since 1.0
+ */
+ public int getStartOffset() {
+ return startOffset;
+ }
+
+ /**
+ * The end offset of a hyperlink's target range.
+ *
+ * @return offset
+ *
+ * @since 1.0
+ */
+ public int getEndOffset() {
+ return endOffset;
+ }
+
+ @Override
+ public int hashCode() {
+ int hash = 5;
+ hash = 29 * hash + Objects.hashCode(this.fileObject);
+ hash = 29 * hash + this.startOffset;
+ hash = 29 * hash + this.endOffset;
+ return hash;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ final HyperlinkLocation other = (HyperlinkLocation) obj;
+ if (this.startOffset != other.startOffset) {
+ return false;
+ }
+ if (this.endOffset != other.endOffset) {
+ return false;
+ }
+ if (!Objects.equals(this.fileObject, other.fileObject)) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return "HyperlinkLocation{" + "fileObject=" + fileObject + ", startOffset=" + startOffset + ", endOffset=" + endOffset + '}';
+ }
+
+ /**
+ * Resolves a hyperlink at the given document offset and returns its target
+ * location(s).
+ *
+ * @param doc document on which to operate.
+ * @param offset offset within document
+ * @return target location(s)
+ *
+ * @since 1.0
+ */
+ @NonNull
+ public static CompletableFuture<List<HyperlinkLocation>> resolve(@NonNull final Document doc, final int offset) {
+ MimePath mimePath = MimePath.parse(DocumentUtilities.getMimeType(doc));
+ CompletableFuture<HyperlinkLocation>[] futures = MimeLookup.getLookup(mimePath).lookupAll(HyperlinkLocationProvider.class).stream()
+ .map(provider -> provider.getHyperlinkLocation(doc, offset)).toArray(CompletableFuture[]::new);
+ return CompletableFuture.allOf(futures).thenApply(value -> {
+ List<HyperlinkLocation> locations = new ArrayList<>(futures.length);
+ for (CompletableFuture<HyperlinkLocation> future : futures) {
+ HyperlinkLocation location = future.getNow(null);
+ if (location != null) {
+ locations.add(location);
+ }
+ }
+ return locations;
+ });
+ }
+}
diff --git a/ide/api.lsp/src/org/netbeans/api/lsp/TextEdit.java b/ide/api.lsp/src/org/netbeans/api/lsp/TextEdit.java
new file mode 100644
index 0000000..2ce85b5
--- /dev/null
+++ b/ide/api.lsp/src/org/netbeans/api/lsp/TextEdit.java
@@ -0,0 +1,70 @@
+/*
+ * 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 org.netbeans.api.annotations.common.NonNull;
+
+/**
+ * Represents a textual edit applicable to a text document.
+ *
+ * @author Dusan Balek
+ * @since 1.0
+ */
+public final class TextEdit {
+
+ private final int start;
+ private final int end;
+ private final String newText;
+
+ public TextEdit(int start, int end, @NonNull String newText) {
+ this.start = start;
+ this.end = end;
+ this.newText = newText;
+ }
+
+ /**
+ * The start offset of the text document range to be manipulated. To insert
+ * text into a document create edit where {@code startOffset == endOffset}.
+ *
+ * @since 1.0
+ */
+ public int getStartOffset() {
+ return start;
+ }
+
+ /**
+ * The end offset of the text document range to be manipulated. To insert
+ * text into a document create edit where {@code startOffset == endOffset}.
+ *
+ * @since 1.0
+ */
+ public int getEndOffset() {
+ return end;
+ }
+
+ /**
+ * The string to be inserted. For delete operations use an empty string.
+ *
+ * @since 1.0
+ */
+ @NonNull
+ public String getNewText() {
+ return newText;
+ }
+}
diff --git a/ide/api.lsp/src/org/netbeans/modules/lsp/CompletionAccessor.java b/ide/api.lsp/src/org/netbeans/modules/lsp/CompletionAccessor.java
new file mode 100644
index 0000000..fddfa49
--- /dev/null
+++ b/ide/api.lsp/src/org/netbeans/modules/lsp/CompletionAccessor.java
@@ -0,0 +1,60 @@
+/*
+ * 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.lsp;
+
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import org.netbeans.api.annotations.common.NonNull;
+import org.netbeans.api.lsp.Completion;
+import org.netbeans.api.lsp.TextEdit;
+import org.openide.util.Exceptions;
+import org.openide.util.Parameters;
+
+
+public abstract class CompletionAccessor {
+
+ private static volatile CompletionAccessor DEFAULT;
+
+ public static synchronized CompletionAccessor getDefault() {
+ CompletionAccessor instance = DEFAULT;
+ if (instance == null) {
+ Class<?> c = Completion.class;
+ try {
+ Class.forName(c.getName(), true, c.getClassLoader());
+ instance = DEFAULT;
+ assert instance != null;
+ } catch (Exception ex) {
+ Exceptions.printStackTrace(ex);
+ }
+ }
+ return instance;
+ }
+
+ public static void setDefault(@NonNull final CompletionAccessor accessor) {
+ Parameters.notNull("accessor", accessor); //NOI18N
+ if (DEFAULT != null) {
+ throw new IllegalStateException("Accessor already initialized");
+ }
+ DEFAULT = accessor;
+ }
+
+ public abstract Completion createCompletion(String label, Completion.Kind kind, List<Completion.Tag> tags, CompletableFuture<String> detail, CompletableFuture<String> documentation,
+ boolean preselect, String sortText, String filterText, String insertText, Completion.TextFormat insertTextFormat,
+ TextEdit textEdit, CompletableFuture<List<TextEdit>> additionalTextEdits, List<Character> commitCharacters);
+}
diff --git a/ide/api.lsp/src/org/netbeans/modules/lsp/HyperlinkLocationAccessor.java b/ide/api.lsp/src/org/netbeans/modules/lsp/HyperlinkLocationAccessor.java
new file mode 100644
index 0000000..5067c69
--- /dev/null
+++ b/ide/api.lsp/src/org/netbeans/modules/lsp/HyperlinkLocationAccessor.java
@@ -0,0 +1,53 @@
+/*
+ * 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.lsp;
+
+import org.netbeans.api.annotations.common.NonNull;
+import org.netbeans.api.lsp.HyperlinkLocation;
+import org.openide.filesystems.FileObject;
+import org.openide.util.Exceptions;
+import org.openide.util.Parameters;
+
+
+public abstract class HyperlinkLocationAccessor {
+
+ private static volatile HyperlinkLocationAccessor DEFAULT;
+
+ public static synchronized HyperlinkLocationAccessor getDefault() {
+ HyperlinkLocationAccessor instance = DEFAULT;
+ if (instance == null) {
+ Class<?> c = HyperlinkLocation.class;
+ try {
+ Class.forName(c.getName(), true, c.getClassLoader());
+ instance = DEFAULT;
+ assert instance != null;
+ } catch (Exception ex) {
+ Exceptions.printStackTrace(ex);
+ }
+ }
+ return instance;
+ }
+
+ public static void setDefault(@NonNull final HyperlinkLocationAccessor accessor) {
+ Parameters.notNull("accessor", accessor); //NOI18N
+ DEFAULT = accessor;
+ }
+
+ public abstract HyperlinkLocation createHyperlinkLocation(@NonNull final FileObject fileObject, int startOffset, int endOffset);
+}
diff --git a/ide/api.lsp/src/org/netbeans/spi/lsp/CompletionCollector.java b/ide/api.lsp/src/org/netbeans/spi/lsp/CompletionCollector.java
new file mode 100644
index 0000000..8691e3c
--- /dev/null
+++ b/ide/api.lsp/src/org/netbeans/spi/lsp/CompletionCollector.java
@@ -0,0 +1,358 @@
+/*
+ * 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.ArrayList;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+import javax.swing.text.Document;
+import org.netbeans.api.annotations.common.NonNull;
+import org.netbeans.api.annotations.common.NullAllowed;
+import org.netbeans.api.lsp.Completion;
+import org.netbeans.api.lsp.TextEdit;
+import org.netbeans.modules.lsp.CompletionAccessor;
+import org.netbeans.spi.editor.mimelookup.MimeLocation;
+
+/**
+ * Interface for computing and collecting completions. Clients can use this interface
+ * to collect completions and send them for presentation outside of NetBeans using
+ * the Language Server Protocol. Implementations of the interface should be registered
+ * in MimeLookup.
+ * <pre>
+ *
+ * {@code @MimeRegistration(mimeType = "text/foo", service = CompletionCollector.class)
+ * public class FooCompletionCollector implements CompletionCollector {
+ * ...
+ * }
+ * }
+ * </pre>
+ *
+ * @author Dusan Balek
+ * @since 1.0
+ */
+@MimeLocation(subfolderName = "CompletionCollectors")
+public interface CompletionCollector {
+
+ /**
+ * Computes and collects completions for a document at a given offset.
+ *
+ * @param doc a text document
+ * @param offset an offset inside the text document
+ * @param context an optional completion context
+ * @param consumer an operation accepting collected completions
+ *
+ * @return true if the list of collected completion is complete
+ *
+ * @since 1.0
+ */
+ public boolean collectCompletions(@NonNull Document doc, int offset, @NullAllowed Completion.Context context, @NonNull Consumer<Completion> consumer);
+
+ /**
+ * Creates a builder for {@link Completion} instances.
+ *
+ * @param label the label of the completion
+ * @return newly created builder
+ *
+ * @since 1.0
+ */
+ public static Builder newBuilder(@NonNull String label) {
+ return new Builder(label);
+ }
+
+ /**
+ * Builder for {@link Completion} instances. Its usage can be illustrated by:
+ * {@codesnippet CompletionTest.FooCompletionCollector#builder}
+ *
+ * @since 1.0
+ */
+ public static final class Builder {
+
+ private String label;
+ private Completion.Kind kind;
+ private List<Completion.Tag> tags;
+ private CompletableFuture<String> detail;
+ private CompletableFuture<String> documentation;
+ private boolean preselect;
+ private String sortText;
+ private String filterText;
+ private String insertText;
+ private Completion.TextFormat insertTextFormat;
+ private TextEdit textEdit;
+ private CompletableFuture<List<TextEdit>> additionalTextEdits;
+ private List<Character> commitCharacters;
+
+ private Builder(@NonNull String label) {
+ this.label = label;
+ }
+
+ /**
+ * The label of this completion. By default also the text that is inserted
+ * when selecting this completion.
+ *
+ * @since 1.0
+ */
+ @NonNull
+ public Builder label(@NonNull String label) {
+ this.label = label;
+ return this;
+ }
+
+ /**
+ * The kind of this completion.
+ *
+ * @since 1.0
+ */
+ @NonNull
+ public Builder kind(@NonNull Completion.Kind kind) {
+ this.kind = kind;
+ return this;
+ }
+
+ /**
+ * Adds tag for this completion.
+ *
+ * @since 1.0
+ */
+ @NonNull
+ public Builder addTag(@NonNull Completion.Tag tag) {
+ if (this.tags == null) {
+ this.tags = new ArrayList<>();
+ }
+ this.tags.add(tag);
+ return this;
+ }
+
+ /**
+ * A human-readable string with additional information about this completion,
+ * like type or symbol information. If computing of a full detail is expensive,
+ * user should use {@link #detail(java.util.function.Supplier)} to defer
+ * computation to the subsequent {@code completionItem/resolve} request.
+ *
+ * @since 1.0
+ */
+ @NonNull
+ public Builder detail(@NonNull String detail) {
+ this.detail = CompletableFuture.completedFuture(detail);
+ return this;
+ }
+
+ /**
+ * A human-readable string with additional information about this completion,
+ * like type or symbol information. Use this method to defer computation
+ * of a full detail to the subsequent {@code completionItem/resolve} request.
+ *
+ * @since 1.0
+ */
+ @NonNull
+ public Builder detail(@NonNull Supplier<String> detail) {
+ this.detail = new LazyCompletableFuture<>(detail);
+ return this;
+ }
+
+ /**
+ * A human-readable string that represents a doc-comment. An HTML format
+ * is supported. If computing of a full documentation is expensive,
+ * user should use {@link #documentation(java.util.function.Supplier)} to
+ * defer computation to the subsequent {@code completionItem/resolve} request.
+ *
+ * @since 1.0
+ */
+ @NonNull
+ public Builder documentation(@NonNull String documentation) {
+ this.documentation = CompletableFuture.completedFuture(documentation);
+ return this;
+ }
+
+ /**
+ * A human-readable string that represents a doc-comment. An HTML format
+ * is supported. Use this method to defer computation of a full documentation
+ * to the subsequent {@code completionItem/resolve} request.
+ *
+ * @since 1.0
+ */
+ @NonNull
+ public Builder documentation(@NonNull Supplier<String> documentation) {
+ this.documentation = new LazyCompletableFuture<>(documentation);
+ return this;
+ }
+
+ /**
+ * Select this completion when showing.
+ *
+ * @since 1.0
+ */
+ @NonNull
+ public Builder preselect(boolean preselect) {
+ this.preselect = preselect;
+ return this;
+ }
+
+ /**
+ * A string that should be used when comparing this completion with other
+ * completions. When {@code null} the label is used as the sort text.
+ *
+ * @since 1.0
+ */
+ @NonNull
+ public Builder sortText(@NonNull String sortText) {
+ this.sortText = sortText;
+ return this;
+ }
+
+ /**
+ * A string that should be used when filtering a set of completions.
+ * When {@code null} the label is used as the filter.
+ *
+ * @since 1.0
+ */
+ @NonNull
+ public Builder filterText(@NonNull String filterText) {
+ this.filterText = filterText;
+ return this;
+ }
+
+ /**
+ * A string that should be inserted into a document when selecting
+ * this completion. When {@code null} the label is used as the insert text.
+ *
+ * @since 1.0
+ */
+ @NonNull
+ public Builder insertText(@NonNull String insertText) {
+ this.insertText = insertText;
+ this.textEdit = null;
+ return this;
+ }
+
+ /**
+ * The format of the insert text. The format applies to both the
+ * {@code insertText} property and the {@code newText} property of a provided
+ * {@code textEdit}. If omitted defaults to {@link TextFormat#PlainText}.
+ *
+ * @since 1.0
+ */
+ @NonNull
+ public Builder insertTextFormat(@NonNull Completion.TextFormat insertTextFormat) {
+ this.insertTextFormat = insertTextFormat;
+ return this;
+ }
+
+ /**
+ * An edit which is applied to a document when selecting this completion.
+ * When an edit is provided the value of {@code insertText} is ignored.
+ * The range of the edit must be a single line range and it must
+ * contain the position at which completion has been requested.
+ *
+ * @since 1.0
+ */
+ @NonNull
+ public Builder textEdit(@NonNull TextEdit textEdit) {
+ this.textEdit = textEdit;
+ this.insertText = null;
+ return this;
+ }
+
+ /**
+ * A list of additional text edits that are applied when selecting this
+ * completion. Edits must not overlap (including the same insert position)
+ * with the main edit nor with themselves.
+ * Additional text edits should be used to change text unrelated to the
+ * current cursor position (for example adding an import statement at the
+ * top of the file if the completion item will insert an unqualified type).
+ * If computing of the additional text edits is expensive, user should use
+ * {@link #additionalTextEdits(java.util.function.Supplier)} to defer
+ * computation to the subsequent {@code completionItem/resolve} request.
+ *
+ * @since 1.0
+ */
+ @NonNull
+ public Builder additionalTextEdits(@NonNull List<TextEdit> additionalTextEdits) {
+ this.additionalTextEdits = CompletableFuture.completedFuture(additionalTextEdits);
+ return this;
+ }
+
+ /**
+ * A list of additional text edits that are applied when selecting this
+ * completion. Edits must not overlap (including the same insert position)
+ * with the main edit nor with themselves.
+ * Additional text edits should be used to change text unrelated to the
+ * current cursor position (for example adding an import statement at the
+ * top of the file if the completion item will insert an unqualified type).
+ * Use this method to defer computation of the additional text edits to
+ * the subsequent {@code completionItem/resolve} request.
+ *
+ * @since 1.0
+ */
+ @NonNull
+ public Builder additionalTextEdits(@NonNull Supplier<List<TextEdit>> additionalTextEdits) {
+ this.additionalTextEdits = new LazyCompletableFuture<>(additionalTextEdits);
+ return this;
+ }
+
+ /**
+ * Adds character that when pressed while this completion is active will
+ * accept it first and then type that character.
+ *
+ * @since 1.0
+ */
+ @NonNull
+ public Builder addCommitCharacter(char commitCharacter) {
+ if (this.commitCharacters == null) {
+ this.commitCharacters = new ArrayList<>();
+ }
+ this.commitCharacters.add(commitCharacter);
+ return this;
+ }
+
+ /**
+ * Builds completion.
+ *
+ * @since 1.0
+ */
+ @NonNull
+ public Completion build() {
+ return CompletionAccessor.getDefault().createCompletion(label, kind,
+ tags, detail, documentation, preselect, sortText, filterText,
+ insertText, insertTextFormat, textEdit, additionalTextEdits,
+ commitCharacters);
+ }
+
+ private static class LazyCompletableFuture<T> extends CompletableFuture<T> {
+
+ private final Supplier<T> supplier;
+
+ private LazyCompletableFuture(Supplier<T> supplier) {
+ this.supplier = supplier;
+ }
+
+ @Override
+ public T get() throws InterruptedException, ExecutionException {
+ try {
+ this.complete(supplier.get());
+ } catch (Exception ex) {
+ this.completeExceptionally(ex);
+ }
+ return super.get();
+ }
+ }
+ }
+}
diff --git a/ide/api.lsp/src/org/netbeans/spi/lsp/HyperlinkLocationProvider.java b/ide/api.lsp/src/org/netbeans/spi/lsp/HyperlinkLocationProvider.java
new file mode 100644
index 0000000..48d760e
--- /dev/null
+++ b/ide/api.lsp/src/org/netbeans/spi/lsp/HyperlinkLocationProvider.java
@@ -0,0 +1,72 @@
+/*
+ * 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 org.netbeans.api.lsp.HyperlinkLocation;
+import java.util.concurrent.CompletableFuture;
+import javax.swing.text.Document;
+import org.netbeans.api.annotations.common.NonNull;
+import org.netbeans.modules.lsp.HyperlinkLocationAccessor;
+import org.netbeans.spi.editor.mimelookup.MimeLocation;
+import org.openide.filesystems.FileObject;
+
+/**
+ * Interface for resolving hyperlink locations. Implementations of the interface
+ * should be registered in MimeLookup.
+ * <pre>
+ *
+ * {@code @MimeRegistration(mimeType = "text/foo", service = HyperlinkLocationProvider.class)
+ * public class FooHyperlinkLocationProvider implements HyperlinkLocationProvider {
+ * ...
+ * }
+ * }
+ * </pre>
+ *
+ * @author Dusan Balek
+ * @since 1.0
+ */
+@MimeLocation(subfolderName = "HyperlinkLocationProviders")
+public interface HyperlinkLocationProvider {
+
+ /**
+ * Resolves a hyperlink at the given document offset and returns its
+ * target location.
+ *
+ * @param doc document on which to operate.
+ * @param offset offset within document
+ * @return target location
+ *
+ * @since 1.0
+ */
+ CompletableFuture<HyperlinkLocation> getHyperlinkLocation(@NonNull Document doc, int offset);
+
+ /**
+ * Creates {@link HyperlinkLocation} instances.
+ *
+ * @param fileObject target file object of the hyperlink
+ * @param startOffset start offset of the hyperlink's target range
+ * @param endOffset end offset of the hyperlink's target range
+ * @return new created instance
+ *
+ * @since 1.0
+ */
+ public static HyperlinkLocation createHyperlinkLocation(@NonNull FileObject fileObject, int startOffset, int endOffset) {
+ return HyperlinkLocationAccessor.getDefault().createHyperlinkLocation(fileObject, startOffset, endOffset);
+ }
+}
diff --git a/ide/api.lsp/test/unit/src/org/netbeans/api/lsp/CompletionTest.java b/ide/api.lsp/test/unit/src/org/netbeans/api/lsp/CompletionTest.java
new file mode 100644
index 0000000..1c0b640
--- /dev/null
+++ b/ide/api.lsp/test/unit/src/org/netbeans/api/lsp/CompletionTest.java
@@ -0,0 +1,131 @@
+/*
+ * 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 java.util.function.Consumer;
+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.CompletionCollector;
+import org.openide.util.Exceptions;
+
+/**
+ *
+ * @author Dusan Balek
+ */
+public class CompletionTest extends NbTestCase {
+
+ public CompletionTest(String name) {
+ super(name);
+ }
+
+ @Override
+ public void setUp () throws Exception {
+ super.setUp();
+ clearWorkDir();
+ MockMimeLookup.setInstances (MimePath.get ("text/foo"), new FooCompletionCollector());
+ }
+
+ public void testCompletionCollect() {
+ Document doc = createDocument("text/foo", "");
+ int offset = 0;
+ // BEGIN: CompletionTest#testCompletionCollect
+
+ // Compute and collect completions for a document at a given offset
+ boolean isComplete = Completion.collect(doc, offset, null, completion -> {
+
+ // completion should never be 'null'
+ assertNotNull(completion);
+
+ // getting completion 'label'
+ String label = completion.getLabel();
+ assertEquals("label", label);
+
+ // getting optional completion 'detail'
+ CompletableFuture<String> detail = completion.getDetail();
+ // check for 'null' value
+ if (detail != null) {
+ // value should be already computed
+ assertTrue(detail.isDone());
+ // getting the value
+ String value = detail.getNow(null);
+ assertEquals("detail", value);
+ }
+
+ // getting optional completion 'documentation'
+ CompletableFuture<String> documentation = completion.getDocumentation();
+ // check for 'null' value
+ if (documentation != null) {
+ // value computation should be deferred
+ assertFalse(documentation.isDone());
+ // getting the value
+ try {
+ String value = documentation.get();
+ assertEquals("documentation", value);
+ } catch (Exception ex) {
+ Exceptions.printStackTrace(ex);
+ }
+ }
+ });
+
+ // END: CompletionTest#testCompletionCollect
+ assertTrue(isComplete);
+ }
+
+ 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 FooCompletionCollector implements CompletionCollector {
+
+ @Override
+ public boolean collectCompletions(Document doc, int offset, Completion.Context context, Consumer<Completion> consumer) {
+ // BEGIN: CompletionTest.FooCompletionCollector#builder
+
+ // Create a builder for creating 'Completion' instance providing its 'label'
+ Completion c = CompletionCollector.newBuilder("label")
+
+ // set completion detail
+ .detail("detail")
+
+ // set completion documentation with deffered computation
+ .documentation(() -> {
+ return "documentation";
+ })
+
+ // create a new 'Completion' instance
+ .build();
+
+ // END: CompletionTest.FooCompletionCollector#builder
+ consumer.accept(c);
+ return true;
+ }
+ }
+}
diff --git a/ide/csl.api/src/org/netbeans/modules/csl/editor/completion/GsfCompletionDoc.java b/ide/csl.api/src/org/netbeans/modules/csl/editor/completion/GsfCompletionDoc.java
index b922d1f..9be44e2 100644
--- a/ide/csl.api/src/org/netbeans/modules/csl/editor/completion/GsfCompletionDoc.java
+++ b/ide/csl.api/src/org/netbeans/modules/csl/editor/completion/GsfCompletionDoc.java
@@ -101,15 +101,12 @@ public class GsfCompletionDoc implements CompletionDocumentation {
this.content = completer.document(controller, elementHandle);
}
}
-
- if (this.content == null) {
- Completion.get().hideDocumentation();
- }
}
public static final GsfCompletionDoc create(ParserResult controller,
ElementHandle elementHandle, Callable<Boolean> cancel) {
- return new GsfCompletionDoc(controller, elementHandle, null, cancel);
+ GsfCompletionDoc doc = new GsfCompletionDoc(controller, elementHandle, null, cancel);
+ return doc.content != null ? doc : null;
}
public String getText() {
diff --git a/ide/csl.api/src/org/netbeans/modules/csl/editor/completion/GsfCompletionProvider.java b/ide/csl.api/src/org/netbeans/modules/csl/editor/completion/GsfCompletionProvider.java
index 766bda6..9c3d961 100644
--- a/ide/csl.api/src/org/netbeans/modules/csl/editor/completion/GsfCompletionProvider.java
+++ b/ide/csl.api/src/org/netbeans/modules/csl/editor/completion/GsfCompletionProvider.java
@@ -561,7 +561,7 @@ public class GsfCompletionProvider implements CompletionProvider {
}
});
// TODO - find some way to show the multiple overloaded methods?
- if (documentation.getText() != null && documentation.getText().length() > 0) {
+ if (documentation != null && documentation.getText() != null && documentation.getText().length() > 0) {
// Make sure we at least pick an alternative that has documentation
break;
}
diff --git a/ide/languages.yaml/src/org/netbeans/modules/languages/yaml/YamlCompletion.java b/ide/languages.yaml/src/org/netbeans/modules/languages/yaml/YamlCompletion.java
index 4ad7da1..6ff3089 100644
--- a/ide/languages.yaml/src/org/netbeans/modules/languages/yaml/YamlCompletion.java
+++ b/ide/languages.yaml/src/org/netbeans/modules/languages/yaml/YamlCompletion.java
@@ -152,17 +152,19 @@ public class YamlCompletion implements CodeCompletionHandler {
StringBuilder sb = new StringBuilder();
try {
- is = new BufferedInputStream(YamlCompletion.class.getResourceAsStream("refcard.html"));
- while (true) {
- int c = is.read();
+ InputStream stream = YamlCompletion.class.getResourceAsStream("refcard.html");
+ if (stream != null) {
+ is = new BufferedInputStream(stream);
+ while (true) {
+ int c = is.read();
- if (c == -1) {
- break;
- }
+ if (c == -1) {
+ break;
+ }
- sb.append((char) c);
+ sb.append((char) c);
+ }
}
-
if (sb.length() > 0) {
refcard = sb.toString();
}
diff --git a/ide/languages.yaml/src/org/netbeans/modules/languages/yaml/YamlParser.java b/ide/languages.yaml/src/org/netbeans/modules/languages/yaml/YamlParser.java
index 6ea7368..6b6d85b 100644
--- a/ide/languages.yaml/src/org/netbeans/modules/languages/yaml/YamlParser.java
+++ b/ide/languages.yaml/src/org/netbeans/modules/languages/yaml/YamlParser.java
@@ -31,10 +31,14 @@ import java.util.regex.Pattern;
import javax.swing.event.ChangeListener;
import org.jruby.util.ByteList;
import org.jvyamlb.Composer;
+import org.jvyamlb.ParserImpl;
import org.jvyamlb.PositioningComposerImpl;
import org.jvyamlb.PositioningParserImpl;
import org.jvyamlb.PositioningScannerImpl;
import org.jvyamlb.ResolverImpl;
+import org.jvyamlb.YAMLConfig;
+import org.jvyamlb.events.Event;
+import org.jvyamlb.exceptions.PositionedComposerException;
import org.jvyamlb.exceptions.PositionedParserException;
import org.jvyamlb.exceptions.PositionedScannerException;
import org.jvyamlb.nodes.Node;
@@ -194,6 +198,7 @@ public class YamlParser extends Parser {
source = replaceCommonSpecialCharacters(source);
source = replaceInlineRegexBrackets(source);
+ List<Node> nodes = new ArrayList<Node>();
try {
if (isTooLarge(source)) {
return resultForTooLargeFile(snapshot);
@@ -248,8 +253,56 @@ public class YamlParser extends Parser {
byteList = new ByteList(out.toByteArray());
}
- Composer composer = new PositioningComposerImpl(new PositioningParserImpl(new PositioningScannerImpl(byteList)), new ResolverImpl());
- List<Node> nodes = new ArrayList<Node>();
+ final List<DefaultError> errors = new ArrayList<>();
+ PositioningScannerImpl scanner = new PositioningScannerImpl(byteList) {
+ @Override
+ protected void scannerException(String when, String what, String note) {
+ try {
+ super.scannerException(when, what, note);
+ } catch (PositionedScannerException pse) {
+ int pos = pse.getPosition().offset;
+ String message = pse.getMessage();
+ if (message != null && message.length() > 0) {
+ errors.add(processError(message, snapshot, pos));
+ }
+ // Move local pointer to the next char to make progress
+ this.pointer++;
+ }
+ }
+ };
+ PositioningParserImpl parser = new PositioningParserImpl(scanner) {
+ @Override
+ protected ParserImpl.ProductionEnvironment getEnvironment(YAMLConfig cfg) {
+ return new PositioningProductionEnvironment(cfg) {
+ @Override
+ protected void parserException(String when, String what, String note, org.jvyamlb.tokens.Token t) {
+ try {
+ super.parserException(when, what, note, t);
+ } catch (PositionedParserException ppe) {
+ int pos = ppe.getPosition().offset;
+ String message = ppe.getMessage();
+ if (message != null && message.length() > 0) {
+ errors.add(processError(message, snapshot, pos));
+ }
+ }
+ }
+ };
+ }
+ };
+ Composer composer = new PositioningComposerImpl(parser, new ResolverImpl()) {
+ @Override
+ protected void composerException(String when, String what, String note, Event e) {
+ try {
+ super.composerException(when, what, note, e);
+ } catch (PositionedComposerException pce) {
+ int pos = pce.getPosition().offset;
+ String message = pce.getMessage();
+ if (message != null && message.length() > 0) {
+ errors.add(processError(message, snapshot, pos));
+ }
+ }
+ }
+ };
Iterator iterator = composer.eachNode();
while (iterator.hasNext()) {
Node node = (Node) iterator.next();
@@ -259,59 +312,39 @@ public class YamlParser extends Parser {
nodes.add(node);
}
- //Object yaml = YAML.load(stream);
- return new YamlParserResult(nodes, this, snapshot, true, byteToUtf8, utf8toByte);
- } catch (Exception ex) {
- int pos = 0;
- if (ex instanceof PositionedParserException) {
- PositionedParserException ppe = (PositionedParserException) ex;
- pos = ppe.getPosition().offset;
- } else if (ex instanceof PositionedScannerException) {
- PositionedScannerException pse = (PositionedScannerException) ex;
- pos = pse.getPosition().offset;
- // The scanner possition is on the next token. We need to reallocate it
- // on the previous token.
- TokenHierarchy th = snapshot.getTokenHierarchy();
- if (th != null) {
- TokenSequence ts = th.tokenSequence();
- if (ts != null) {
- ts.move(pos);
- ts.moveNext();
- Token token = ts.token();
- // don't move the error, when there is more values on the line. See #171633
- if (token != null && token.text().toString().indexOf('{') == -1 && ts.movePrevious()) {
- pos = ts.offset();
- }
- }
- }
+ YamlParserResult result = new YamlParserResult(nodes, this, snapshot, true, byteToUtf8, utf8toByte);
+ if (!errors.isEmpty()) {
+ result.addError(errors.get(0));
}
-
+ return result;
+ } catch (Exception ex) {
YamlParserResult result = new YamlParserResult(Collections.<Node>emptyList(), this, snapshot, false, null, null);
String message = ex.getMessage();
if (message != null && message.length() > 0) {
- // Strip off useless prefixes to make errors more readable
- if (message.startsWith("ScannerException null ")) { // NOI18N
- message = message.substring(22);
- } else if (message.startsWith("ParserException ")) { // NOI18N
- message = message.substring(16);
- }
- // Capitalize sentences
- char firstChar = message.charAt(0);
- char upcasedChar = Character.toUpperCase(firstChar);
- if (firstChar != upcasedChar) {
- message = upcasedChar + message.substring(1);
- }
-
- // FIXME this can violate contract of DefaultError (null fo)
- DefaultError error = new DefaultError(null, message, null, snapshot.getSource().getFileObject(),
- pos, pos, Severity.ERROR);
- result.addError(error);
+ result.addError(processError(message, snapshot, 0));
}
-
return result;
}
}
+ private DefaultError processError(String message, Snapshot snapshot, int pos) {
+ // Strip off useless prefixes to make errors more readable
+ if (message.startsWith("ScannerException null ")) { // NOI18N
+ message = message.substring(22);
+ } else if (message.startsWith("ParserException ")) { // NOI18N
+ message = message.substring(16);
+ }
+ // Capitalize sentences
+ char firstChar = message.charAt(0);
+ char upcasedChar = Character.toUpperCase(firstChar);
+ if (firstChar != upcasedChar) {
+ message = upcasedChar + message.substring(1);
+ }
+ // FIXME this can violate contract of DefaultError (null fo)
+ return new DefaultError(null, message, null, snapshot.getSource().getFileObject(),
+ pos, pos, Severity.ERROR);
+ }
+
@Override
public void parse(Snapshot snapshot, Task task, SourceModificationEvent event) throws ParseException {
diff --git a/ide/libs.bytelist/external/binaries-list b/ide/libs.bytelist/external/binaries-list
index 0e0b70c1..e3ec14a 100644
--- a/ide/libs.bytelist/external/binaries-list
+++ b/ide/libs.bytelist/external/binaries-list
@@ -14,5 +14,4 @@
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
-80294E59315B314D57782DC37F983AE5B29C2E4C org.jruby.extras:bytelist:1.0.15
-E2C76A19F00128BB1806207E2989139BFB45F49D org.jruby.jcodings:jcodings:1.0.18
+80294E59315B314D57782DC37F983AE5B29C2E4C org.jruby.extras:bytelist:1.0.15
\ No newline at end of file
diff --git a/ide/libs.bytelist/nbproject/project.properties b/ide/libs.bytelist/nbproject/project.properties
index 20a62cb..4e011ca 100644
--- a/ide/libs.bytelist/nbproject/project.properties
+++ b/ide/libs.bytelist/nbproject/project.properties
@@ -17,5 +17,4 @@
is.autoload=true
release.external/bytelist-1.0.15.jar=modules/ext/bytelist-1.0.15.jar
-release.external/jcodings-1.0.18.jar=modules/ext/jcodings-1.0.18.jar
-spec.version.base=0.41.0
+spec.version.base=0.42.0
diff --git a/ide/libs.bytelist/nbproject/project.xml b/ide/libs.bytelist/nbproject/project.xml
index b735374..92cc333 100644
--- a/ide/libs.bytelist/nbproject/project.xml
+++ b/ide/libs.bytelist/nbproject/project.xml
@@ -24,7 +24,15 @@
<configuration>
<data xmlns="http://www.netbeans.org/ns/nb-module-project/2">
<code-name-base>org.netbeans.libs.bytelist</code-name-base>
- <module-dependencies/>
+ <module-dependencies>
+ <dependency>
+ <code-name-base>org.netbeans.libs.jcodings</code-name-base>
+ <run-dependency>
+ <release-version>1</release-version>
+ <specification-version>0.1</specification-version>
+ </run-dependency>
+ </dependency>
+ </module-dependencies>
<!--
<friend-packages>
<friend>org.jruby</friend>
@@ -39,10 +47,6 @@
<package>org.jruby.util</package>
</public-packages>
<class-path-extension>
- <runtime-relative-path>ext/jcodings-1.0.18.jar</runtime-relative-path>
- <binary-origin>external/jcodings-1.0.18.jar</binary-origin>
- </class-path-extension>
- <class-path-extension>
<runtime-relative-path>ext/bytelist-1.0.15.jar</runtime-relative-path>
<binary-origin>external/bytelist-1.0.15.jar</binary-origin>
</class-path-extension>
diff --git a/ide/libs.jcodings/build.xml b/ide/libs.jcodings/build.xml
new file mode 100644
index 0000000..c6a8ce1
--- /dev/null
+++ b/ide/libs.jcodings/build.xml
@@ -0,0 +1,24 @@
+<?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.
+
+-->
+<project name="ide/libs.jcodings" default="build" basedir=".">
+ <import file="../../nbbuild/templates/projectized.xml"/>
+</project>
diff --git a/ide/libs.bytelist/external/binaries-list b/ide/libs.jcodings/external/binaries-list
similarity index 92%
copy from ide/libs.bytelist/external/binaries-list
copy to ide/libs.jcodings/external/binaries-list
index 0e0b70c1..28eb079 100644
--- a/ide/libs.bytelist/external/binaries-list
+++ b/ide/libs.jcodings/external/binaries-list
@@ -14,5 +14,4 @@
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
-80294E59315B314D57782DC37F983AE5B29C2E4C org.jruby.extras:bytelist:1.0.15
E2C76A19F00128BB1806207E2989139BFB45F49D org.jruby.jcodings:jcodings:1.0.18
diff --git a/ide/libs.bytelist/external/jcodings-1.0.18-license.txt b/ide/libs.jcodings/external/jcodings-1.0.18-license.txt
similarity index 100%
rename from ide/libs.bytelist/external/jcodings-1.0.18-license.txt
rename to ide/libs.jcodings/external/jcodings-1.0.18-license.txt
diff --git a/ide/libs.jcodings/manifest.mf b/ide/libs.jcodings/manifest.mf
new file mode 100644
index 0000000..af1e111
--- /dev/null
+++ b/ide/libs.jcodings/manifest.mf
@@ -0,0 +1,4 @@
+Manifest-Version: 1.0
+OpenIDE-Module: org.netbeans.libs.jcodings/1
+OpenIDE-Module-Localizing-Bundle: org/netbeans/libs/jcodings/Bundle.properties
+OpenIDE-Module-Specification-Version: 0.1
diff --git a/ide/libs.bytelist/nbproject/project.properties b/ide/libs.jcodings/nbproject/project.properties
similarity index 90%
copy from ide/libs.bytelist/nbproject/project.properties
copy to ide/libs.jcodings/nbproject/project.properties
index 20a62cb..0c9d012 100644
--- a/ide/libs.bytelist/nbproject/project.properties
+++ b/ide/libs.jcodings/nbproject/project.properties
@@ -16,6 +16,4 @@
# under the License.
is.autoload=true
-release.external/bytelist-1.0.15.jar=modules/ext/bytelist-1.0.15.jar
release.external/jcodings-1.0.18.jar=modules/ext/jcodings-1.0.18.jar
-spec.version.base=0.41.0
diff --git a/ide/libs.bytelist/nbproject/project.xml b/ide/libs.jcodings/nbproject/project.xml
similarity index 64%
copy from ide/libs.bytelist/nbproject/project.xml
copy to ide/libs.jcodings/nbproject/project.xml
index b735374..09be4f9 100644
--- a/ide/libs.bytelist/nbproject/project.xml
+++ b/ide/libs.jcodings/nbproject/project.xml
@@ -22,30 +22,16 @@
<project xmlns="http://www.netbeans.org/ns/project/1">
<type>org.netbeans.modules.apisupport.project</type>
<configuration>
- <data xmlns="http://www.netbeans.org/ns/nb-module-project/2">
- <code-name-base>org.netbeans.libs.bytelist</code-name-base>
+ <data xmlns="http://www.netbeans.org/ns/nb-module-project/3">
+ <code-name-base>org.netbeans.libs.jcodings</code-name-base>
<module-dependencies/>
- <!--
- <friend-packages>
- <friend>org.jruby</friend>
- <friend>org.netbeans.modules.libs.jvyamlb</friend>
- <friend>org.netbeans.modules.languages.yaml</friend>
- <friend>org.netbeans.modules.ruby</friend>
- <friend>org.netbeans.modules.ruby.hints</friend>
- <package>org.jruby.util</package>
- </friend-packages>
--->
<public-packages>
- <package>org.jruby.util</package>
+ <subpackages>org.jcodings</subpackages>
</public-packages>
<class-path-extension>
<runtime-relative-path>ext/jcodings-1.0.18.jar</runtime-relative-path>
<binary-origin>external/jcodings-1.0.18.jar</binary-origin>
</class-path-extension>
- <class-path-extension>
- <runtime-relative-path>ext/bytelist-1.0.15.jar</runtime-relative-path>
- <binary-origin>external/bytelist-1.0.15.jar</binary-origin>
- </class-path-extension>
</data>
</configuration>
</project>
diff --git a/ide/libs.bytelist/nbproject/project.properties b/ide/libs.jcodings/src/org/netbeans/libs/jcodings/Bundle.properties
similarity index 75%
copy from ide/libs.bytelist/nbproject/project.properties
copy to ide/libs.jcodings/src/org/netbeans/libs/jcodings/Bundle.properties
index 20a62cb..b730623 100644
--- a/ide/libs.bytelist/nbproject/project.properties
+++ b/ide/libs.jcodings/src/org/netbeans/libs/jcodings/Bundle.properties
@@ -15,7 +15,8 @@
# specific language governing permissions and limitations
# under the License.
-is.autoload=true
-release.external/bytelist-1.0.15.jar=modules/ext/bytelist-1.0.15.jar
-release.external/jcodings-1.0.18.jar=modules/ext/jcodings-1.0.18.jar
-spec.version.base=0.41.0
+OpenIDE-Module-Name=JCodings Library
+OpenIDE-Module-Display-Category=Libraries
+OpenIDE-Module-Short-Description=JCodings Library
+OpenIDE-Module-Long-Description=\
+ This module bundles the JCodings library used by TextMate Lexer and the JRuby ByteList.
diff --git a/ide/textmate.lexer/external/binaries-list b/ide/textmate.lexer/external/binaries-list
index bfdb182..4bb066b 100644
--- a/ide/textmate.lexer/external/binaries-list
+++ b/ide/textmate.lexer/external/binaries-list
@@ -14,6 +14,5 @@
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
-E2C76A19F00128BB1806207E2989139BFB45F49D org.jruby.jcodings:jcodings:1.0.18
655CC3ABA1BC9DBDD653F28937BEC16F3E9C4CEC org.jruby.joni:joni:2.1.11
109763C0803F3EAC2372B4E4A9A0D7BED9CAB5BC eu.doppel-helix.netbeans.lib.tm4e:org.eclipse.tm4e.core:0.4.1-pack1
diff --git a/ide/textmate.lexer/external/jcodings-1.0.18-license.txt b/ide/textmate.lexer/external/jcodings-1.0.18-license.txt
deleted file mode 100644
index cd273db..0000000
--- a/ide/textmate.lexer/external/jcodings-1.0.18-license.txt
+++ /dev/null
@@ -1,23 +0,0 @@
-Name: jcodings
-Version: 1.0.18
-License: MIT-nocopyright
-Description: Needed by TextMate support for Eclipse
-Origin: https://github.com/jruby/jcodings
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of
-this software and associated documentation files (the "Software"), to deal in
-the Software without restriction, including without limitation the rights to
-use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
-of the Software, and to permit persons to whom the Software is furnished to do
-so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
diff --git a/ide/textmate.lexer/nbproject/project.properties b/ide/textmate.lexer/nbproject/project.properties
index 45278cc..442ebd7 100644
--- a/ide/textmate.lexer/nbproject/project.properties
+++ b/ide/textmate.lexer/nbproject/project.properties
@@ -20,6 +20,5 @@ javac.compilerargs=-Xlint -Xlint:-serial
javadoc.arch=${basedir}/arch.xml
javadoc.apichanges=${basedir}/apichanges.xml
release.external/joni-2.1.11.jar=modules/ext/joni-2.1.11.jar
-release.external/jcodings-1.0.18.jar=modules/ext/jcodings-1.0.18.jar
release.external/org.eclipse.tm4e.core-0.4.1-pack1.jar=modules/ext/org.eclipse.tm4e.core-0.4.1-pack1.jar
-spec.version.base=1.11.0
+spec.version.base=1.12.0
diff --git a/ide/textmate.lexer/nbproject/project.xml b/ide/textmate.lexer/nbproject/project.xml
index ada1613..974264d 100644
--- a/ide/textmate.lexer/nbproject/project.xml
+++ b/ide/textmate.lexer/nbproject/project.xml
@@ -40,6 +40,13 @@
</run-dependency>
</dependency>
<dependency>
+ <code-name-base>org.netbeans.libs.jcodings</code-name-base>
+ <run-dependency>
+ <release-version>1</release-version>
+ <specification-version>0.1</specification-version>
+ </run-dependency>
+ </dependency>
+ <dependency>
<code-name-base>org.netbeans.modules.editor.lib2</code-name-base>
<build-prerequisite/>
<compile-dependency/>
@@ -197,10 +204,6 @@
<binary-origin>external/joni-2.1.11.jar</binary-origin>
</class-path-extension>
<class-path-extension>
- <runtime-relative-path>ext/jcodings-1.0.18.jar</runtime-relative-path>
- <binary-origin>external/jcodings-1.0.18.jar</binary-origin>
- </class-path-extension>
- <class-path-extension>
<runtime-relative-path>ext/org.eclipse.tm4e.core-0.4.1-pack1.jar</runtime-relative-path>
<binary-origin>external/org.eclipse.tm4e.core-0.4.1-pack1.jar</binary-origin>
</class-path-extension>
diff --git a/java/java.editor/nbproject/project.xml b/java/java.editor/nbproject/project.xml
index 45663f5..eaa3a89 100644
--- a/java/java.editor/nbproject/project.xml
+++ b/java/java.editor/nbproject/project.xml
@@ -53,6 +53,15 @@
</run-dependency>
</dependency>
<dependency>
+ <code-name-base>org.netbeans.api.lsp</code-name-base>
+ <build-prerequisite/>
+ <compile-dependency/>
+ <run-dependency>
+ <release-version>1</release-version>
+ <specification-version>1.0</specification-version>
+ </run-dependency>
+ </dependency>
+ <dependency>
<code-name-base>org.netbeans.api.progress</code-name-base>
<build-prerequisite/>
<compile-dependency/>
@@ -248,7 +257,7 @@
<compile-dependency/>
<run-dependency>
<release-version>1</release-version>
- <specification-version>1.20</specification-version>
+ <specification-version>1.58</specification-version>
</run-dependency>
</dependency>
<dependency>
diff --git a/java/java.editor/src/org/netbeans/modules/editor/java/GoToSupport.java b/java/java.editor/src/org/netbeans/modules/editor/java/GoToSupport.java
index 675511b..287c926 100644
--- a/java/java.editor/src/org/netbeans/modules/editor/java/GoToSupport.java
+++ b/java/java.editor/src/org/netbeans/modules/editor/java/GoToSupport.java
@@ -25,6 +25,7 @@ import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.IdentifierTree;
import com.sun.source.tree.ImportTree;
import com.sun.source.tree.InstanceOfTree;
+import com.sun.source.tree.LineMap;
import com.sun.source.tree.MemberSelectTree;
import com.sun.source.tree.MethodInvocationTree;
import com.sun.source.tree.MethodTree;
@@ -44,14 +45,18 @@ import com.sun.source.util.Trees;
import java.awt.Toolkit;
import java.awt.event.KeyEvent;
import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumSet;
import java.util.Iterator;
import java.util.List;
+import java.util.Objects;
import java.util.Set;
import java.util.concurrent.Callable;
+import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -92,6 +97,7 @@ import org.netbeans.api.java.source.ui.ElementOpen;
import org.netbeans.api.lexer.Token;
import org.netbeans.api.lexer.TokenHierarchy;
import org.netbeans.api.lexer.TokenSequence;
+import org.netbeans.api.lsp.HyperlinkLocation;
import org.netbeans.api.progress.ProgressUtils;
import org.netbeans.editor.ext.ToolTipSupport;
import org.netbeans.lib.editor.hyperlink.spi.HyperlinkType;
@@ -103,15 +109,13 @@ import org.netbeans.modules.parsing.api.Source;
import org.netbeans.modules.parsing.api.UserTask;
import org.netbeans.modules.parsing.spi.ParseException;
import org.netbeans.modules.parsing.spi.Parser.Result;
+import org.netbeans.spi.lsp.HyperlinkLocationProvider;
import org.openide.awt.HtmlBrowser;
import org.openide.awt.StatusDisplayer;
import org.openide.filesystems.FileObject;
import org.openide.loaders.DataObject;
import org.openide.util.Exceptions;
import org.openide.util.NbBundle;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.util.Objects;
/**
*
@@ -161,6 +165,51 @@ public class GoToSupport {
throw new IllegalStateException(ex);
}
}
+
+ public static CompletableFuture<HyperlinkLocation> getGoToLocation(final Document doc, final int offset) {
+ try {
+ final FileObject fo = getFileObject(doc);
+ if (fo != null) {
+ final GoToTarget[] target = new GoToTarget[1];
+ final LineMap[] lineMap = new LineMap[1];
+
+ ParserManager.parse(Collections.singleton (Source.create(doc)), new UserTask() {
+ @Override
+ public void run(ResultIterator resultIterator) throws Exception {
+ Result res = resultIterator.getParserResult (offset);
+ CompilationController controller = res != null ? CompilationController.get(res) : null;
+ if (controller == null || controller.toPhase(Phase.RESOLVED).compareTo(Phase.RESOLVED) < 0) {
+ return;
+ }
+
+ Context resolved = resolveContext(controller, doc, offset, false, false);
+
+ if (resolved == null) {
+ target[0] = new GoToTarget(-1, -1, null, null, null, null, null, false);
+ } else {
+ target[0] = computeGoToTarget(controller, resolved, offset);
+ }
+
+ lineMap[0] = controller.getCompilationUnit().getLineMap();
+ }
+ });
+ if (target[0] != null && target[0].success) {
+ if (target[0].offsetToOpen < 0) {
+ CompletableFuture<ElementOpen.Location> future = ElementOpen.getLocation(target[0].cpInfo, target[0].elementToOpen, target[0].resourceName);
+ return future.thenApply(location -> {
+ return location != null ? HyperlinkLocationProvider.createHyperlinkLocation(location.getFileObject(), location.getStartOffset(), location.getEndOffset()) : null;
+ });
+ }
+ int start = target[0].nameSpan != null ? target[0].nameSpan[0] : target[0].offsetToOpen;
+ int end = target[0].nameSpan != null ? target[0].nameSpan[1] : target[0].endPos;
+ return CompletableFuture.completedFuture(HyperlinkLocationProvider.createHyperlinkLocation(fo, start, end));
+ }
+ }
+ return CompletableFuture.completedFuture(null);
+ } catch (ParseException ex) {
+ throw new IllegalStateException(ex);
+ }
+ }
private static boolean isError(Element el) {
return el == null || el.asType() == null || el.asType().getKind() == TypeKind.ERROR;
diff --git a/java/java.editor/src/org/netbeans/modules/editor/java/JavaCompletionCollector.java b/java/java.editor/src/org/netbeans/modules/editor/java/JavaCompletionCollector.java
new file mode 100644
index 0000000..0dc17e0
--- /dev/null
+++ b/java/java.editor/src/org/netbeans/modules/editor/java/JavaCompletionCollector.java
@@ -0,0 +1,548 @@
+/*
+ * 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 com.sun.source.tree.Scope;
+import com.sun.source.util.TreePath;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.TypeParameterElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.ArrayType;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.ExecutableType;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.Elements;
+import javax.swing.text.Document;
+import org.netbeans.api.editor.mimelookup.MimeRegistration;
+import org.netbeans.api.java.lexer.JavaTokenId;
+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.GeneratorUtilities;
+import org.netbeans.api.java.source.JavaSource;
+import org.netbeans.api.java.source.ModificationResult;
+import org.netbeans.api.java.source.Task;
+import org.netbeans.api.java.source.WorkingCopy;
+import org.netbeans.api.java.source.support.ReferencesCount;
+import org.netbeans.api.java.source.ui.ElementJavadoc;
+import org.netbeans.api.lexer.TokenSequence;
+import org.netbeans.api.lsp.Completion;
+import org.netbeans.api.lsp.TextEdit;
+import org.netbeans.modules.java.completion.JavaCompletionTask;
+import org.netbeans.modules.java.editor.codegen.GeneratorUtils;
+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.filesystems.FileObject;
+import org.openide.util.Exceptions;
+
+/**
+ *
+ * @author Dusan Balek
+ */
+@MimeRegistration(mimeType = "text/x-java", service = CompletionCollector.class)
+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 = resultIterator.getSnapshot().getTokenHierarchy().tokenSequence(JavaTokenId.language());
+ if (ts.move(offset) == 0 || !ts.moveNext()) {
+ if (!ts.movePrevious()) {
+ ts.moveNext();
+ }
+ }
+ int len = offset - ts.offset();
+ boolean allCompletion = 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, ts.offset()), allCompletion ? EnumSet.of(JavaCompletionTask.Options.ALL_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);
+ }
+ }
+ });
+ } catch (ParseException ex) {
+ Exceptions.printStackTrace(ex);
+ }
+ return ret.get();
+ }
+
+ private static class ItemFactoryImpl implements JavaCompletionTask.ItemFactory<Completion> {
+
+ private 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"));
+ private static final int DEPRECATED = 10;
+ private final Document doc;
+ private final int offset;
+ private final CompilationInfo info;
+ private final Scope scope;
+
+ public ItemFactoryImpl(CompilationInfo info, int offset) throws IOException {
+ this.offset = offset;
+ this.info = info;
+ this.doc = info.getDocument();
+ this.scope = info.getTrees().getScope(info.getTreeUtilities().pathFor(offset));
+ }
+
+ @Override
+ public Completion createKeywordItem(String kwd, String postfix, int substitutionOffset, boolean smartType) {
+ return CompletionCollector.newBuilder(kwd)
+ .kind(Completion.Kind.Keyword)
+ .sortText(String.format("%4d%s", smartType ? 670 : 1670, kwd))
+ .insertText(kwd + postfix)
+ .build();
+ }
+
+ @Override
+ public Completion createPackageItem(String pkgFQN, int substitutionOffset, boolean inPackageStatement) {
+ final String simpleName = pkgFQN.substring(pkgFQN.lastIndexOf('.') + 1);
+ return CompletionCollector.newBuilder(simpleName)
+ .kind(Completion.Kind.Folder)
+ .sortText(String.format("%4d%s#%s", 1900, simpleName, pkgFQN))
+ .insertText(simpleName + (inPackageStatement ? "" : "."))
+ .build();
+ }
+
+ @Override
+ public Completion createTypeItem(CompilationInfo info, TypeElement elem, DeclaredType type, int substitutionOffset, ReferencesCount referencesCount, boolean isDeprecated, boolean insideNew, boolean addTypeVars, boolean addSimpleName, boolean smartType, boolean autoImportEnclosingType) {
+ String name = elem.getQualifiedName().toString();
+ int idx = name.lastIndexOf('.');
+ String pkgName = idx < 0 ? "" : name.substring(0, idx);
+ CompletionCollector.Builder builder = CompletionCollector.newBuilder(elem.getSimpleName().toString())
+ .kind(elementKind2CompletionItemKind(elem.getKind()))
+ .sortText(String.format("%4d%s#%2d#%s", smartType ? 800 : 1800, elem.getSimpleName().toString(), Utilities.getImportanceLevel(name), pkgName))
+ .detail(name);
+ ElementHandle<TypeElement> handle = SUPPORTED_ELEMENT_KINDS.contains(elem.getKind().name()) ? ElementHandle.create(elem) : null;
+ if (handle != null) {
+ builder.documentation(getDocumentation(doc, handle))
+ .additionalTextEdits(addImport(doc, handle));
+ }
+ if (isDeprecated) {
+ builder.addTag(Completion.Tag.Deprecated);
+ }
+ return builder.build();
+ }
+
+ @Override
+ public Completion createTypeItem(ElementHandle<TypeElement> handle, EnumSet<ElementKind> kinds, int substitutionOffset, ReferencesCount referencesCount, Source source, boolean insideNew, boolean addTypeVars, boolean afterExtends) {
+ TypeElement te = handle.resolve(info);
+ if (te != null && info.getTrees().isAccessible(scope, te)) {
+ String name = handle.getQualifiedName();
+ int idx = name.lastIndexOf('.');
+ String pkgName = idx < 0 ? "" : name.substring(0, idx);
+ return CompletionCollector.newBuilder(te.getSimpleName().toString())
+ .kind(elementKind2CompletionItemKind(handle.getKind()))
+ .sortText(String.format("%4d%s#%2d#%s", 1800, te.getSimpleName().toString(), Utilities.getImportanceLevel(name), pkgName))
+ .detail(name)
+ .documentation(getDocumentation(doc, handle))
+ .additionalTextEdits(addImport(doc, handle))
+ .build();
+ }
+ return null;
+ }
+
+ @Override
+ public Completion createArrayItem(CompilationInfo info, ArrayType type, int substitutionOffset, ReferencesCount referencesCount, Elements elements) {
+ return null; //TODO: fill
+ }
+
+ @Override
+ public Completion createTypeParameterItem(TypeParameterElement elem, int substitutionOffset) {
+ return CompletionCollector.newBuilder(elem.getSimpleName().toString())
+ .kind(elementKind2CompletionItemKind(elem.getKind()))
+ .sortText(String.format("%4d%s", 1700, elem.getSimpleName().toString()))
+ .build();
+ }
+
+ @Override
+ public Completion createVariableItem(CompilationInfo info, VariableElement elem, TypeMirror type, int substitutionOffset, ReferencesCount referencesCount, boolean isInherited, boolean isDeprecated, boolean smartType, int assignToVarOffset) {
+ int priority = elem.getKind() == ElementKind.ENUM_CONSTANT || elem.getKind() == ElementKind.FIELD ? smartType ? 300 : 1300 : smartType ? 200 : 1200;
+ CompletionCollector.Builder builder = CompletionCollector.newBuilder(elem.getSimpleName().toString())
+ .kind(elementKind2CompletionItemKind(elem.getKind()))
+ .sortText(String.format("%4d%s", priority, elem.getSimpleName().toString()));
+ ElementHandle<VariableElement> handle = SUPPORTED_ELEMENT_KINDS.contains(elem.getKind().name()) ? ElementHandle.create(elem) : null;
+ if (handle != null) {
+ builder.documentation(getDocumentation(doc, handle));
+ }
+ if (isDeprecated) {
+ builder.addTag(Completion.Tag.Deprecated);
+ }
+ return builder.build();
+ }
+
+ @Override
+ public Completion createVariableItem(CompilationInfo info, String varName, int substitutionOffset, boolean newVarName, boolean smartType) {
+ return CompletionCollector.newBuilder(varName)
+ .kind(Completion.Kind.Variable)
+ .sortText(String.format("%4d%s", smartType ? 200 : 1200, varName))
+ .build();
+ }
+
+ @Override
+ public Completion createExecutableItem(CompilationInfo info, ExecutableElement elem, ExecutableType type, int substitutionOffset, ReferencesCount referencesCount, boolean isInherited, boolean isDeprecated, boolean inImport, boolean addSemicolon, boolean smartType, int assignToVarOffset, boolean memberRef) {
+ Iterator<? extends VariableElement> it = elem.getParameters().iterator();
+ Iterator<? extends TypeMirror> tIt = type.getParameterTypes().iterator();
+ StringBuilder label = new StringBuilder();
+ String sep = "";
+ label.append(elem.getSimpleName());
+ label.append("(");
+ StringBuilder sortParams = new StringBuilder();
+ sortParams.append('(');
+ int cnt = 0;
+ while(it.hasNext() && tIt.hasNext()) {
+ TypeMirror tm = tIt.next();
+ if (tm == null) {
+ break;
+ }
+ label.append(sep);
+ String paramTypeName = Utilities.getTypeName(info, tm, false, elem.isVarArgs() && !tIt.hasNext()).toString();
+ label.append(paramTypeName);
+ label.append(' ');
+ label.append(it.next().getSimpleName());
+ sep = ", ";
+ sortParams.append(paramTypeName);
+ if (tIt.hasNext()) {
+ sortParams.append(',');
+ }
+ cnt++;
+ }
+ label.append(") : ");
+ sortParams.append(')');
+ TypeMirror retType = type.getReturnType();
+ label.append(Utilities.getTypeName(info, retType, false).toString());
+ StringBuilder insertText = new StringBuilder();
+ insertText.append(elem.getSimpleName());
+ insertText.append("(");
+ if (elem.getParameters().isEmpty()) {
+ insertText.append(")");
+ }
+ int priority = elem.getKind() == ElementKind.METHOD ? smartType ? 500 : 1500 : smartType ? 650 : 1650;
+ CompletionCollector.Builder builder = CompletionCollector.newBuilder(label.toString())
+ .kind(elementKind2CompletionItemKind(elem.getKind()))
+ .insertText(insertText.toString())
+ .sortText(String.format("%4d%s#%2d%s", priority, elem.getSimpleName().toString(), cnt, sortParams));
+ ElementHandle<ExecutableElement> handle = SUPPORTED_ELEMENT_KINDS.contains(elem.getKind().name()) ? ElementHandle.create(elem) : null;
+ if (handle != null) {
+ builder.documentation(getDocumentation(doc, handle));
+ }
+ if (isDeprecated) {
+ builder.addTag(Completion.Tag.Deprecated);
+ }
+ return builder.build();
+ }
+
+ @Override
+ public Completion createThisOrSuperConstructorItem(CompilationInfo info, ExecutableElement elem, ExecutableType type, int substitutionOffset, boolean isDeprecated, String name) {
+ Completion item = createExecutableItem(info, elem, type, substitutionOffset, null, false, isDeprecated, false, false, false, -1, false);
+ String simpleName = name != null ? name : elem.getEnclosingElement().getSimpleName().toString();
+ int idx = item.getLabel().indexOf('(');
+ String label = simpleName + item.getLabel().substring(idx);
+ idx = item.getInsertText().indexOf('(');
+ String insertText = simpleName + item.getInsertText().substring(idx);
+ CompletionCollector.Builder builder = CompletionCollector.newBuilder(label)
+ .kind(Completion.Kind.Constructor)
+ .insertText(insertText)
+ .sortText(String.format("%4d%s", name != null ? 1550 : 1650, item.getSortText().substring(4)));
+ ElementHandle<ExecutableElement> handle = SUPPORTED_ELEMENT_KINDS.contains(elem.getKind().name()) ? ElementHandle.create(elem) : null;
+ if (handle != null) {
+ builder.documentation(getDocumentation(doc, handle));
+ }
+ return builder.build();
+ }
+
+ @Override
+ public Completion createOverrideMethodItem(CompilationInfo info, ExecutableElement elem, ExecutableType type, int substitutionOffset, boolean implement) {
+ Completion item = createExecutableItem(info, elem, type, substitutionOffset, null, false, false, false, false, false, -1, false);
+ CompletionCollector.Builder builder = CompletionCollector.newBuilder(String.format("%s - %s", item.getLabel(), implement ? "implement" : "override"))
+ .kind(elementKind2CompletionItemKind(elem.getKind()));
+ ElementHandle<ExecutableElement> handle = SUPPORTED_ELEMENT_KINDS.contains(elem.getKind().name()) ? ElementHandle.create(elem) : null;
+ if (handle != null) {
+ builder.documentation(getDocumentation(doc, handle));
+ }
+ try {
+ List<TextEdit> textEdits = modify2TextEdits(JavaSource.forFileObject(info.getFileObject()), wc -> {
+ wc.toPhase(JavaSource.Phase.ELEMENTS_RESOLVED);
+ TreePath tp = wc.getTreeUtilities().pathFor(offset);
+ if (implement) {
+ GeneratorUtils.generateAbstractMethodImplementation(wc, tp, elem, offset);
+ } else {
+ GeneratorUtils.generateMethodOverride(wc, tp, elem, offset);
+ }
+ });
+ if (!textEdits.isEmpty()) {
+ builder.textEdit(textEdits.get(0));
+ }
+ } catch (IOException ex) {
+ }
+ return builder.build();
+ }
+
+ @Override
+ public Completion createGetterSetterMethodItem(CompilationInfo info, VariableElement elem, TypeMirror type, int substitutionOffset, String name, boolean setter) {
+ return null; //TODO: fill
+ }
+
+ @Override
+ public Completion createDefaultConstructorItem(TypeElement elem, int substitutionOffset, boolean smartType) {
+ return null; //TODO: fill
+ }
+
+ @Override
+ public Completion createParametersItem(CompilationInfo info, ExecutableElement elem, ExecutableType type, int substitutionOffset, boolean isDeprecated, int activeParamIndex, String name) {
+ return null; //TODO: fill
+ }
+
+ @Override
+ public Completion createAnnotationItem(CompilationInfo info, TypeElement elem, DeclaredType type, int substitutionOffset, ReferencesCount referencesCount, boolean isDeprecated) {
+ return null; //TODO: fill
+ }
+
+ @Override
+ public Completion createAttributeItem(CompilationInfo info, ExecutableElement elem, ExecutableType type, int substitutionOffset, boolean isDeprecated) {
+ StringBuilder insertText = new StringBuilder();
+ insertText.append(elem.getSimpleName());
+ insertText.append("=");
+ CompletionCollector.Builder builder = CompletionCollector.newBuilder(elem.getSimpleName().toString())
+ .kind(Completion.Kind.Property)
+ .insertText(insertText.toString())
+ .insertTextFormat(Completion.TextFormat.PlainText)
+ .sortText(String.format("%4d%s", isDeprecated ? 100 + DEPRECATED : 100, elem.getSimpleName().toString()));
+ ElementHandle<ExecutableElement> handle = SUPPORTED_ELEMENT_KINDS.contains(elem.getKind().name()) ? ElementHandle.create(elem) : null;
+ if (handle != null) {
+ builder.documentation(getDocumentation(doc, handle));
+ }
+ if (isDeprecated) {
+ builder.addTag(Completion.Tag.Deprecated);
+ }
+ return builder.build();
+ }
+
+ @Override
+ public Completion createAttributeValueItem(CompilationInfo info, String value, String documentation, TypeElement element, int substitutionOffset, ReferencesCount referencesCount) {
+ return CompletionCollector.newBuilder(value)
+ .kind(Completion.Kind.Text)
+ .sortText(value)
+ .documentation(documentation)
+ .build();
+ }
+
+ private static final Object KEY_IMPORT_TEXT_EDITS = new Object();
+
+ @Override
+ public Completion createStaticMemberItem(CompilationInfo info, DeclaredType type, Element memberElem, TypeMirror memberType, boolean multipleVersions, int substitutionOffset, boolean isDeprecated, boolean addSemicolon) {
+ //TODO: prefer static imports (but would be much slower?)
+ //TODO: should be resolveImport instead of addImports:
+ Map<Element, List<TextEdit>> imports = (Map<Element, List<TextEdit>>) info.getCachedValue(KEY_IMPORT_TEXT_EDITS);
+ if (imports == null) {
+ info.putCachedValue(KEY_IMPORT_TEXT_EDITS, imports = new HashMap<>(), CompilationInfo.CacheClearPolicy.ON_TASK_END);
+ }
+ List<TextEdit> currentClassImport = imports.computeIfAbsent(type.asElement(), toImport -> {
+ try {
+ return modify2TextEdits(JavaSource.forFileObject(info.getFileObject()), wc -> {
+ wc.toPhase(JavaSource.Phase.ELEMENTS_RESOLVED);
+ wc.rewrite(info.getCompilationUnit(), GeneratorUtilities.get(wc).addImports(wc.getCompilationUnit(), new HashSet<>(Arrays.asList(toImport))));
+ });
+ } catch (IOException ex) {
+ return Collections.emptyList();
+ }
+ });
+ String label = type.asElement().getSimpleName() + "." + memberElem.getSimpleName();
+ String sortText = memberElem.getSimpleName().toString();
+ if (memberElem.getKind().isField()) {
+ sortText += String.format("#%s", Utilities.getTypeName(info, type, false)); //NOI18N
+ } else {
+ StringBuilder sortParams = new StringBuilder();
+ sortParams.append('(');
+ int cnt = 0;
+ Iterator<? extends TypeMirror> tIt = ((ExecutableType)memberType).getParameterTypes().iterator();
+ while(tIt.hasNext()) {
+ TypeMirror tm = tIt.next();
+ if (tm == null) {
+ break;
+ }
+ sortParams.append(Utilities.getTypeName(info, tm, false, ((ExecutableElement)memberElem).isVarArgs() && !tIt.hasNext()).toString());
+ if (tIt.hasNext()) {
+ sortParams.append(',');
+ }
+ cnt++;
+ }
+ sortParams.append(')');
+ sortText += String.format("#%2d#%s#s", cnt, sortParams.toString(), Utilities.getTypeName(info, type, false)); //NOI18N
+ }
+ CompletionCollector.Builder builder = CompletionCollector.newBuilder(label)
+ .kind(elementKind2CompletionItemKind(memberElem.getKind()))
+ .insertText(label)
+ .additionalTextEdits(currentClassImport)
+ .sortText(String.format("%4d%s", memberElem.getKind().isField() ? 720 : 750, sortText));
+ ElementHandle<Element> handle = SUPPORTED_ELEMENT_KINDS.contains(memberElem.getKind().name()) ? ElementHandle.create(memberElem) : null;
+ if (handle != null) {
+ builder.documentation(getDocumentation(doc, handle));
+ }
+ if (isDeprecated) {
+ builder.addTag(Completion.Tag.Deprecated);
+ }
+ return builder.build();
+ }
+
+ @Override
+ public Completion createStaticMemberItem(ElementHandle<TypeElement> handle, String name, int substitutionOffset, boolean addSemicolon, ReferencesCount referencesCount, Source source) {
+ return null; //TODO: fill
+ }
+
+ @Override
+ public Completion createChainedMembersItem(CompilationInfo info, List<? extends Element> chainedElems, List<? extends TypeMirror> chainedTypes, int substitutionOffset, boolean isDeprecated, boolean addSemicolon) {
+ return null; //TODO: fill
+ }
+
+ @Override
+ public Completion createInitializeAllConstructorItem(CompilationInfo info, boolean isDefault, Iterable<? extends VariableElement> fields, ExecutableElement superConstructor, TypeElement parent, int substitutionOffset) {
+ return null; //TODO: fill
+ }
+
+ private static Supplier<String> getDocumentation(Document doc, ElementHandle<?> handle) {
+ return () -> {
+ try {
+ String[] ret = new String[1];
+ ParserManager.parse(Collections.singletonList(Source.create(doc)), new UserTask() {
+ public void run (ResultIterator resultIterator) throws Exception {
+ CompilationInfo info = CompilationInfo.get(resultIterator.getParserResult());
+ if (info != null) {
+ Element element = handle.resolve(info);
+ if (element != null) {
+ ret[0] = ElementJavadoc.create(info, element, null).getText();
+ }
+ }
+ }
+ });
+ return ret[0];
+ } catch (ParseException ex) {
+ throw new RuntimeException(ex);
+ }
+ };
+ }
+
+ private static Supplier<List<TextEdit>> addImport(Document doc, ElementHandle<?> handle) {
+ return () -> {
+ try {
+ return modify2TextEdits(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)));
+ }
+ });
+ } catch (IOException ex) {
+ throw new RuntimeException(ex);
+ }
+ };
+ }
+
+ private 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;
+ }
+ }
+
+ private static List<TextEdit> modify2TextEdits(JavaSource js, Task<WorkingCopy> task) throws IOException {
+ FileObject[] file = new FileObject[1];
+ ModificationResult changes = js.runModificationTask(wc -> {
+ task.run(wc);
+ file[0] = wc.getFileObject();
+ });
+ List<? extends ModificationResult.Difference> diffs = changes.getDifferences(file[0]);
+ if (diffs == null) {
+ return Collections.emptyList();
+ }
+ return diffs.stream().map(diff -> {
+ String newText = diff.getNewText();
+ return new TextEdit(diff.getStartPosition().getOffset(), diff.getEndPosition().getOffset(), newText != null ? newText : "");
+ }).collect(Collectors.toList());
+ }
+ }
+}
diff --git a/java/java.editor/src/org/netbeans/modules/java/editor/hyperlink/JavaHyperlinkProvider.java b/java/java.editor/src/org/netbeans/modules/java/editor/hyperlink/JavaHyperlinkProvider.java
index ed69d9e..74030ee 100644
--- a/java/java.editor/src/org/netbeans/modules/java/editor/hyperlink/JavaHyperlinkProvider.java
+++ b/java/java.editor/src/org/netbeans/modules/java/editor/hyperlink/JavaHyperlinkProvider.java
@@ -21,13 +21,17 @@ package org.netbeans.modules.java.editor.hyperlink;
import java.util.EnumSet;
import java.util.Set;
+import java.util.concurrent.CompletableFuture;
import javax.swing.text.Document;
import javax.swing.text.JTextComponent;
import org.netbeans.api.editor.EditorRegistry;
+import org.netbeans.api.editor.mimelookup.MimeRegistration;
+import org.netbeans.api.lsp.HyperlinkLocation;
import org.netbeans.lib.editor.hyperlink.spi.HyperlinkProviderExt;
import org.netbeans.lib.editor.hyperlink.spi.HyperlinkType;
import org.netbeans.modules.editor.java.GoToSupport;
import org.netbeans.modules.java.editor.overridden.GoToImplementation;
+import org.netbeans.spi.lsp.HyperlinkLocationProvider;
/**
* Implementation of the hyperlink provider for java language.
@@ -39,22 +43,26 @@ import org.netbeans.modules.java.editor.overridden.GoToImplementation;
* @author Jan Lahoda
*/
public final class JavaHyperlinkProvider implements HyperlinkProviderExt {
-
+
public JavaHyperlinkProvider() {
}
+ @Override
public Set<HyperlinkType> getSupportedHyperlinkTypes() {
return EnumSet.of(HyperlinkType.GO_TO_DECLARATION, HyperlinkType.ALT_HYPERLINK);
}
+ @Override
public boolean isHyperlinkPoint(Document doc, int offset, HyperlinkType type) {
return getHyperlinkSpan(doc, offset, type) != null;
}
+ @Override
public int[] getHyperlinkSpan(Document doc, int offset, HyperlinkType type) {
return GoToSupport.getIdentifierOrLambdaArrowSpan(doc, offset, null);
}
+ @Override
public void performClickAction(Document doc, int offset, HyperlinkType type) {
switch (type) {
case GO_TO_DECLARATION:
@@ -62,7 +70,6 @@ public final class JavaHyperlinkProvider implements HyperlinkProviderExt {
break;
case ALT_HYPERLINK:
JTextComponent focused = EditorRegistry.focusedComponent();
-
if (focused != null && focused.getDocument() == doc) {
focused.setCaretPosition(offset);
GoToImplementation.goToImplementation(focused);
@@ -71,8 +78,17 @@ public final class JavaHyperlinkProvider implements HyperlinkProviderExt {
}
}
+ @Override
public String getTooltipText(Document doc, int offset, HyperlinkType type) {
return GoToSupport.getGoToElementTooltip(doc, offset, false, type);
}
+ @MimeRegistration(mimeType = "text/x-java", service = HyperlinkLocationProvider.class)
+ public static class LocationProvider implements HyperlinkLocationProvider {
+
+ @Override
+ public CompletableFuture<HyperlinkLocation> getHyperlinkLocation(Document doc, int offset) {
+ return GoToSupport.getGoToLocation(doc, offset);
+ }
+ }
}
diff --git a/java/java.lsp.server/nbcode/nbproject/platform.properties b/java/java.lsp.server/nbcode/nbproject/platform.properties
index 2723dce..ea16447 100644
--- a/java/java.lsp.server/nbcode/nbproject/platform.properties
+++ b/java/java.lsp.server/nbcode/nbproject/platform.properties
@@ -17,6 +17,7 @@
branding.token=nbcode
cluster.path=\
+ ${nbplatform.active.dir}/enterprise:\
${nbplatform.active.dir}/extide:\
${nbplatform.active.dir}/ide:\
${nbplatform.active.dir}/java:\
@@ -71,7 +72,6 @@ disabled.modules=\
org.netbeans.lib.nbjshell,\
org.netbeans.lib.nbjshell9,\
org.netbeans.lib.terminalemulator,\
- org.netbeans.libs.bytelist,\
org.netbeans.libs.cglib,\
org.netbeans.libs.commons_net,\
org.netbeans.libs.git,\
@@ -82,7 +82,6 @@ disabled.modules=\
org.netbeans.libs.jshell.compile,\
org.netbeans.libs.jsr223,\
org.netbeans.libs.jstestdriver,\
- org.netbeans.libs.jvyamlb,\
org.netbeans.libs.nbi.ant,\
org.netbeans.libs.nbi.engine,\
org.netbeans.libs.plist,\
@@ -248,7 +247,6 @@ disabled.modules=\
org.netbeans.modules.languages.diff,\
org.netbeans.modules.languages.ini,\
org.netbeans.modules.languages.manifest,\
- org.netbeans.modules.languages.yaml,\
org.netbeans.modules.localhistory,\
org.netbeans.modules.localtasks,\
org.netbeans.modules.lsp.client,\
@@ -351,7 +349,114 @@ disabled.modules=\
org.openide.execution.compat8,\
org.openide.options,\
org.openide.util.enumerations,\
- org.openidex.util
+ org.openidex.util,\
+ org.netbeans.api.web.webmodule,\
+ org.netbeans.libs.amazon,\
+ org.netbeans.libs.commons_fileupload,\
+ org.netbeans.libs.elimpl,\
+ org.netbeans.libs.glassfish_logging,\
+ org.netbeans.libs.jstl,\
+ org.netbeans.modules.cloud.amazon,\
+ org.netbeans.modules.cloud.common,\
+ org.netbeans.modules.el.lexer,\
+ org.netbeans.modules.glassfish.common,\
+ org.netbeans.modules.glassfish.eecommon,\
+ org.netbeans.modules.glassfish.javaee,\
+ org.netbeans.modules.glassfish.tooling,\
+ org.netbeans.modules.gradle.javaee,\
+ org.netbeans.modules.j2ee.ant,\
+ org.netbeans.modules.j2ee.api.ejbmodule,\
+ org.netbeans.modules.j2eeapis,\
+ org.netbeans.modules.j2ee.clientproject,\
+ org.netbeans.modules.j2ee.common,\
+ org.netbeans.modules.j2ee.core,\
+ org.netbeans.modules.j2ee.dd,\
+ org.netbeans.modules.j2ee.ddloaders,\
+ org.netbeans.modules.j2ee.dd.webservice,\
+ org.netbeans.modules.j2ee.earproject,\
+ org.netbeans.modules.j2ee.ejbcore,\
+ org.netbeans.modules.j2ee.ejbjarproject,\
+ org.netbeans.modules.j2ee.ejbrefactoring,\
+ org.netbeans.modules.j2ee.ejbverification,\
+ org.netbeans.modules.j2ee.genericserver,\
+ org.netbeans.modules.j2ee.kit,\
+ org.netbeans.modules.j2ee.platform,\
+ org.netbeans.modules.j2eeserver,\
+ org.netbeans.modules.j2ee.sun.appsrv,\
+ org.netbeans.modules.j2ee.sun.dd,\
+ org.netbeans.modules.j2ee.sun.ddui,\
+ org.netbeans.modules.jakartaee8.api,\
+ org.netbeans.modules.jakartaee8.platform,\
+ org.netbeans.modules.javaee7.api,\
+ org.netbeans.modules.javaee8.api,\
+ org.netbeans.modules.javaee.api,\
+ org.netbeans.modules.javaee.beanvalidation,\
+ org.netbeans.modules.javaee.project,\
+ org.netbeans.modules.javaee.resources,\
+ org.netbeans.modules.javaee.specs.support,\
+ org.netbeans.modules.javaee.wildfly,\
+ org.netbeans.modules.jellytools.enterprise,\
+ org.netbeans.modules.jsp.lexer,\
+ org.netbeans.modules.maven.j2ee,\
+ org.netbeans.modules.maven.jaxws,\
+ org.netbeans.modules.payara.common,\
+ org.netbeans.modules.payara.eecommon,\
+ org.netbeans.modules.payara.jakartaee,\
+ org.netbeans.modules.payara.micro,\
+ org.netbeans.modules.payara.tooling,\
+ org.netbeans.modules.profiler.j2ee,\
+ org.netbeans.modules.projectimport.eclipse.web,\
+ org.netbeans.modules.servletjspapi,\
+ org.netbeans.modules.spring.webmvc,\
+ org.netbeans.modules.tomcat5,\
+ org.netbeans.modules.web.beans,\
+ org.netbeans.modules.web.bootsfaces,\
+ org.netbeans.modules.web.client.rest,\
+ org.netbeans.modules.web.core,\
+ org.netbeans.modules.web.core.syntax,\
+ org.netbeans.modules.web.debug,\
+ org.netbeans.modules.web.el,\
+ org.netbeans.modules.web.freeform,\
+ org.netbeans.modules.web.jsf12,\
+ org.netbeans.modules.web.jsf12ri,\
+ org.netbeans.modules.web.jsf20,\
+ org.netbeans.modules.web.jsfapi,\
+ org.netbeans.modules.web.jsf.editor,\
+ org.netbeans.modules.web.jsf.icefaces,\
+ org.netbeans.modules.web.jsf,\
+ org.netbeans.modules.web.jsf.kit,\
+ org.netbeans.modules.web.jsf.navigation,\
+ org.netbeans.modules.web.jsf.richfaces,\
+ org.netbeans.modules.web.jspparser,\
+ org.netbeans.modules.web.kit,\
+ org.netbeans.modules.weblogic.common,\
+ org.netbeans.modules.web.monitor,\
+ org.netbeans.modules.web.primefaces,\
+ org.netbeans.modules.web.project,\
+ org.netbeans.modules.web.refactoring,\
+ org.netbeans.modules.websocket,\
+ org.netbeans.modules.web.struts,\
+ org.netbeans.modules.websvc.clientapi,\
+ org.netbeans.modules.websvc.core,\
+ org.netbeans.modules.websvc.customization,\
+ org.netbeans.modules.websvc.design,\
+ org.netbeans.modules.websvc.editor.hints,\
+ org.netbeans.modules.websvc.jaxwsapi,\
+ org.netbeans.modules.websvc.jaxws.lightapi,\
+ org.netbeans.modules.websvc.jaxwsmodel,\
+ org.netbeans.modules.websvc.kit,\
+ org.netbeans.modules.websvc.manager,\
+ org.netbeans.modules.websvc.metro.lib,\
+ org.netbeans.modules.websvc.owsm,\
+ org.netbeans.modules.websvc.projectapi,\
+ org.netbeans.modules.websvc.restapi,\
+ org.netbeans.modules.websvc.rest,\
+ org.netbeans.modules.websvc.restkit,\
+ org.netbeans.modules.websvc.restlib,\
+ org.netbeans.modules.websvc.saas.codegen.j2ee,\
+ org.netbeans.modules.websvc.utilities,\
+ org.netbeans.modules.websvc.websvcapi,\
+ org.netbeans.modules.websvc.wsstackapi
nbplatform.active=nball
suite.dir=${basedir}
nbplatform.nball.netbeans.dest.dir=${suite.dir}/../../../nbbuild/netbeans
diff --git a/java/java.lsp.server/nbproject/project.xml b/java/java.lsp.server/nbproject/project.xml
index 048173d..d78ddd8 100644
--- a/java/java.lsp.server/nbproject/project.xml
+++ b/java/java.lsp.server/nbproject/project.xml
@@ -87,6 +87,15 @@
</run-dependency>
</dependency>
<dependency>
+ <code-name-base>org.netbeans.api.lsp</code-name-base>
+ <build-prerequisite/>
+ <compile-dependency/>
+ <run-dependency>
+ <release-version>1</release-version>
+ <specification-version>1.0</specification-version>
+ </run-dependency>
+ </dependency>
+ <dependency>
<code-name-base>org.netbeans.api.progress</code-name-base>
<build-prerequisite/>
<compile-dependency/>
@@ -174,29 +183,29 @@
</run-dependency>
</dependency>
<dependency>
- <code-name-base>org.netbeans.modules.gsf.testrunner</code-name-base>
+ <code-name-base>org.netbeans.modules.extexecution.base</code-name-base>
<build-prerequisite/>
<compile-dependency/>
<run-dependency>
<release-version>2</release-version>
- <specification-version>2.22</specification-version>
+ <specification-version>1.17</specification-version>
</run-dependency>
</dependency>
<dependency>
- <code-name-base>org.netbeans.modules.gsf.testrunner.ui</code-name-base>
+ <code-name-base>org.netbeans.modules.gsf.testrunner</code-name-base>
<build-prerequisite/>
<compile-dependency/>
<run-dependency>
- <specification-version>1.22</specification-version>
+ <release-version>2</release-version>
+ <specification-version>2.22</specification-version>
</run-dependency>
</dependency>
<dependency>
- <code-name-base>org.netbeans.modules.extexecution.base</code-name-base>
+ <code-name-base>org.netbeans.modules.gsf.testrunner.ui</code-name-base>
<build-prerequisite/>
<compile-dependency/>
<run-dependency>
- <release-version>2</release-version>
- <specification-version>1.17</specification-version>
+ <specification-version>1.22</specification-version>
</run-dependency>
</dependency>
<dependency>
diff --git a/java/java.lsp.server/script/etc/nbcode.clusters b/java/java.lsp.server/script/etc/nbcode.clusters
index 622f750..2151943 100644
--- a/java/java.lsp.server/script/etc/nbcode.clusters
+++ b/java/java.lsp.server/script/etc/nbcode.clusters
@@ -4,4 +4,5 @@ extide
webcommon
java
extra
+enterprise
nbcode
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 db228e1..908c9d2 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
@@ -24,7 +24,6 @@ import com.sun.source.tree.ClassTree;
import com.sun.source.tree.CompilationUnitTree;
import com.sun.source.tree.LineMap;
import com.sun.source.tree.MethodTree;
-import com.sun.source.tree.Scope;
import com.sun.source.tree.Tree;
import com.sun.source.tree.Tree.Kind;
import com.sun.source.tree.VariableTree;
@@ -40,7 +39,6 @@ import java.net.URI;
import java.net.URL;
import java.time.Instant;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
@@ -57,6 +55,7 @@ import java.util.concurrent.CompletableFuture;
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;
import java.util.function.IntFunction;
import java.util.logging.Level;
@@ -67,13 +66,6 @@ import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
-import javax.lang.model.element.TypeParameterElement;
-import javax.lang.model.element.VariableElement;
-import javax.lang.model.type.ArrayType;
-import javax.lang.model.type.DeclaredType;
-import javax.lang.model.type.ExecutableType;
-import javax.lang.model.type.TypeMirror;
-import javax.lang.model.util.Elements;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.StyledDocument;
@@ -85,9 +77,9 @@ import org.eclipse.lsp4j.CodeLensParams;
import org.eclipse.lsp4j.Command;
import org.eclipse.lsp4j.CompletionItem;
import org.eclipse.lsp4j.CompletionItemKind;
+import org.eclipse.lsp4j.CompletionItemTag;
import org.eclipse.lsp4j.CompletionList;
import org.eclipse.lsp4j.CompletionParams;
-import org.eclipse.lsp4j.CompletionTriggerKind;
import org.eclipse.lsp4j.CreateFile;
import org.eclipse.lsp4j.DefinitionParams;
import org.eclipse.lsp4j.Diagnostic;
@@ -138,13 +130,10 @@ import org.eclipse.lsp4j.services.LanguageClient;
import org.eclipse.lsp4j.services.LanguageClientAware;
import org.eclipse.lsp4j.services.TextDocumentService;
import org.netbeans.api.annotations.common.CheckForNull;
-import org.netbeans.api.java.classpath.ClassPath;
import org.netbeans.api.java.lexer.JavaTokenId;
-import org.netbeans.api.java.queries.SourceJavadocAttacher;
-import org.netbeans.api.java.source.ClasspathInfo;
+import org.netbeans.api.java.project.JavaProjectConstants;
import org.netbeans.api.java.source.CompilationController;
import org.netbeans.api.java.source.CompilationInfo;
-import org.netbeans.api.java.source.CompilationInfo.CacheClearPolicy;
import org.netbeans.api.java.source.ElementHandle;
import org.netbeans.api.java.source.GeneratorUtilities;
import org.netbeans.api.java.source.JavaSource;
@@ -155,16 +144,19 @@ 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.support.ReferencesCount;
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;
+import org.netbeans.api.lsp.HyperlinkLocation;
+import org.netbeans.api.project.Project;
+import org.netbeans.api.project.ProjectUtils;
+import org.netbeans.api.project.SourceGroup;
+import org.netbeans.api.project.Sources;
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.editor.java.Utilities;
import org.netbeans.modules.gsf.testrunner.ui.api.TestMethodController.TestMethod;
-import org.netbeans.modules.java.completion.JavaCompletionTask;
-import org.netbeans.modules.java.completion.JavaCompletionTask.Options;
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;
@@ -188,8 +180,6 @@ import org.netbeans.modules.java.hints.spiimpl.options.HintsSettings;
import org.netbeans.modules.java.lsp.server.LspServerState;
import org.netbeans.modules.java.lsp.server.Utils;
import org.netbeans.modules.java.lsp.server.debugging.utils.ErrorUtilities;
-import org.netbeans.modules.java.source.ElementHandleAccessor;
-import org.netbeans.modules.java.source.ui.ElementOpenAccessor;
import org.netbeans.modules.java.testrunner.ui.spi.ComputeTestMethods;
import org.netbeans.modules.parsing.api.ParserManager;
import org.netbeans.modules.parsing.api.ResultIterator;
@@ -215,7 +205,6 @@ import org.netbeans.spi.editor.hints.ErrorDescription;
import org.netbeans.spi.editor.hints.Fix;
import org.netbeans.spi.editor.hints.LazyFixList;
import org.netbeans.spi.editor.hints.Severity;
-import org.netbeans.spi.java.classpath.support.ClassPathSupport;
import org.netbeans.spi.java.hints.JavaFix;
import org.openide.cookies.EditorCookie;
import org.openide.filesystems.FileObject;
@@ -285,8 +274,12 @@ public class TextDocumentServiceImpl implements TextDocumentService, LanguageCli
}
}
+ private List<Completion> lastCompletions = null;
+
@Override
public CompletableFuture<Either<List<CompletionItem>, CompletionList>> completion(CompletionParams params) {
+ lastCompletions = new ArrayList<>();
+ AtomicInteger index = new AtomicInteger(0);
final CompletionList completionList = new CompletionList();
// shortcut: if the projects are not yet initialized, return empty:
if (server.openedProjects().getNow(null) == null) {
@@ -301,62 +294,85 @@ public class TextDocumentServiceImpl implements TextDocumentService, LanguageCli
EditorCookie ec = file.getLookup().lookup(EditorCookie.class);
Document doc = ec.openDocument();
final int caret = Utils.getOffset(doc, params.getPosition());
- ParserManager.parse(Collections.singletonList(Source.create(doc)), new UserTask() {
- @Override
- public void run(ResultIterator resultIterator) throws Exception {
- TokenSequence<JavaTokenId> ts = resultIterator.getSnapshot().getTokenHierarchy().tokenSequence(JavaTokenId.language());
- if (ts.move(caret) == 0 || !ts.moveNext()) {
- if (!ts.movePrevious()) {
- ts.moveNext();
- }
+ List<CompletionItem> items = new ArrayList<>();
+ Completion.Context context = params.getContext() != null
+ ? new Completion.Context(Completion.TriggerKind.valueOf(params.getContext().getTriggerKind().name()),
+ params.getContext().getTriggerCharacter() == null || params.getContext().getTriggerCharacter().isEmpty() ? null : params.getContext().getTriggerCharacter().charAt(0))
+ : null;
+ boolean isComplete = Completion.collect(doc, caret, context, completion -> {
+ CompletionItem item = new CompletionItem(completion.getLabel());
+ if (completion.getKind() != null) {
+ item.setKind(CompletionItemKind.valueOf(completion.getKind().name()));
+ }
+ if (completion.getTags() != null) {
+ item.setTags(completion.getTags().stream().map(tag -> CompletionItemTag.valueOf(tag.name())).collect(Collectors.toList()));
+ }
+ if (completion.getDetail() != null && completion.getDetail().isDone()) {
+ item.setDetail(completion.getDetail().getNow(null));
+ }
+ if (completion.getDocumentation() != null && completion.getDocumentation().isDone()) {
+ String documentation = completion.getDocumentation().getNow(null);
+ if (documentation != null) {
+ MarkupContent markup = new MarkupContent();
+ markup.setKind("markdown");
+ markup.setValue(html2MD(documentation));
+ item.setDocumentation(markup);
}
- int len = caret - ts.offset();
- boolean allCompletion = params.getContext() != null && params.getContext().getTriggerKind() == CompletionTriggerKind.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<CompletionItem> task = JavaCompletionTask.create(caret, new ItemFactoryImpl(client, controller, uri, ts.offset()), allCompletion ? EnumSet.of(Options.ALL_COMPLETION) : EnumSet.noneOf(Options.class), () -> false);
- task.run(resultIterator);
- List<CompletionItem> results = task.getResults();
- if (results != null) {
- for (Iterator<CompletionItem> it = results.iterator(); it.hasNext();) {
- CompletionItem item = it.next();
- if (item == null) {
- it.remove();
- }
- }
- completionList.setItems(results);
+ }
+ if (completion.isPreselect()) {
+ item.setPreselect(true);
+ }
+ item.setSortText(completion.getSortText());
+ item.setFilterText(completion.getFilterText());
+ item.setInsertText(completion.getInsertText());
+ if (completion.getInsertTextFormat() != null) {
+ item.setInsertTextFormat(InsertTextFormat.valueOf(completion.getInsertTextFormat().name()));
+ }
+ org.netbeans.api.lsp.TextEdit edit = completion.getTextEdit();
+ if (edit != null) {
+ item.setTextEdit(new TextEdit(new Range(Utils.createPosition(file, edit.getStartOffset()), Utils.createPosition(file, edit.getEndOffset())), edit.getNewText()));
+ }
+ if (completion.getAdditionalTextEdits() != null && completion.getAdditionalTextEdits().isDone()) {
+ List<org.netbeans.api.lsp.TextEdit> additionalTextEdits = completion.getAdditionalTextEdits().getNow(null);
+ if (additionalTextEdits != null) {
+ item.setAdditionalTextEdits(additionalTextEdits.stream().map(ed -> {
+ return new TextEdit(new Range(Utils.createPosition(file, ed.getStartOffset()), Utils.createPosition(file, ed.getEndOffset())), ed.getNewText());
+ }).collect(Collectors.toList()));
}
- completionList.setIsIncomplete(task.hasAdditionalClasses());
}
+ if (completion.getCommitCharacters() != null) {
+ item.setCommitCharacters(completion.getCommitCharacters().stream().map(ch -> ch.toString()).collect(Collectors.toList()));
+ }
+ lastCompletions.add(completion);
+ item.setData(new CompletionData(uri, index.getAndIncrement()));
+ items.add(item);
});
+ if (!isComplete) {
+ completionList.setIsIncomplete(true);
+ }
+ completionList.setItems(items);
return CompletableFuture.completedFuture(Either.<List<CompletionItem>, CompletionList>forRight(completionList));
- } catch (IOException | ParseException ex) {
+ } catch (IOException ex) {
throw new IllegalStateException(ex);
}
}
public static final class CompletionData {
public String uri;
- public int offset;
- public String kind;
- public String[] elementHandle;
+ public int index;
public CompletionData() {
}
- public CompletionData(String uri, int offset, String kind, String[] elementHandle) {
+ public CompletionData(String uri, int index) {
this.uri = uri;
- this.offset = offset;
- this.kind = kind;
- this.elementHandle = elementHandle;
+ this.index = index;
}
@Override
public String toString() {
- return "CompletionData{" + "uri=" + uri + ", kind=" + kind + ", elementHandle=" + elementHandle + '}';
+ return "CompletionData{" + "uri=" + uri + ", index=" + index + '}';
}
-
}
@Override
@@ -364,438 +380,55 @@ public class TextDocumentServiceImpl implements TextDocumentService, LanguageCli
this.client = (NbCodeLanguageClient)client;
}
- private static class ItemFactoryImpl implements JavaCompletionTask.ItemFactory<CompletionItem> {
-
- private static final int DEPRECATED = 10;
- private final LanguageClient client;
- private final String uri;
- private final int offset;
- private final CompilationInfo info;
- private final Scope scope;
-
- public ItemFactoryImpl(LanguageClient client, CompilationInfo info, String uri, int offset) {
- this.client = client;
- this.uri = uri;
- this.offset = offset;
- this.info = info;
- this.scope = info.getTrees().getScope(info.getTreeUtilities().pathFor(offset));
- }
-
- private 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"));
-
- private void setCompletionData(CompletionItem ci, Element el) {
- if (SUPPORTED_ELEMENT_KINDS.contains(el.getKind().name())) {
- setCompletionData(ci, ElementHandle.create(el));
- }
- }
-
- private void setCompletionData(CompletionItem ci, ElementHandle handle) {
- ci.setData(new CompletionData(uri, offset, handle.getKind().name(), SourceUtils.getJVMSignature(handle)));
- }
-
- @Override
- public CompletionItem createKeywordItem(String kwd, String postfix, int substitutionOffset, boolean smartType) {
- CompletionItem item = new CompletionItem(kwd);
- item.setKind(CompletionItemKind.Keyword);
- item.setSortText(String.format("%4d%s", smartType ? 670 : 1670, kwd)); //NOI18N
- return item;
- }
-
- @Override
- public CompletionItem createPackageItem(String pkgFQN, int substitutionOffset, boolean inPackageStatement) {
- final String simpleName = pkgFQN.substring(pkgFQN.lastIndexOf('.') + 1);
- CompletionItem item = new CompletionItem(simpleName);
- item.setKind(CompletionItemKind.Folder);
- item.setSortText(String.format("%4d%s#%s", 1900, simpleName, pkgFQN)); //NOI18N
- return item;
- }
-
- @Override
- public CompletionItem createTypeItem(CompilationInfo info, TypeElement elem, DeclaredType type, int substitutionOffset, ReferencesCount referencesCount, boolean isDeprecated, boolean insideNew, boolean addTypeVars, boolean addSimpleName, boolean smartType, boolean autoImportEnclosingType) {
- CompletionItem item = new CompletionItem(elem.getSimpleName().toString());
- item.setKind(elementKind2CompletionItemKind(elem.getKind()));
- String name = elem.getQualifiedName().toString();
- int idx = name.lastIndexOf('.');
- String pkgName = idx < 0 ? "" : name.substring(0, idx);
- if (!pkgName.isEmpty()) {
- item.setDetail(name.substring(0, idx));
- }
- item.setSortText(String.format("%4d%s#%2d#%s", smartType ? 800 : 1800, elem.getSimpleName().toString(), Utilities.getImportanceLevel(name), pkgName)); //NOI18N
- setCompletionData(item, elem);
- return item;
- }
-
- @Override
- public CompletionItem createTypeItem(ElementHandle<TypeElement> handle, EnumSet<ElementKind> kinds, int substitutionOffset, ReferencesCount referencesCount, Source source, boolean insideNew, boolean addTypeVars, boolean afterExtends) {
- TypeElement te = handle.resolve(info);
- if (te != null && info.getTrees().isAccessible(scope, te)) {
- CompletionItem item = new CompletionItem(te.getSimpleName().toString());
- String name = handle.getQualifiedName();
- int idx = name.lastIndexOf('.');
- String pkgName = idx < 0 ? "" : name.substring(0, idx);
- if (!pkgName.isEmpty()) {
- item.setDetail(pkgName);
- }
- item.setKind(elementKind2CompletionItemKind(handle.getKind()));
- item.setSortText(String.format("%4d%s#%2d#%s", 1800, te.getSimpleName().toString(), Utilities.getImportanceLevel(name), pkgName)); //NOI18N
- setCompletionData(item, handle);
- return item;
- }
- return null;
- }
-
- @Override
- public CompletionItem createArrayItem(CompilationInfo info, ArrayType type, int substitutionOffset, ReferencesCount referencesCount, Elements elements) {
- return null; //TODO: fill
- }
-
- @Override
- public CompletionItem createTypeParameterItem(TypeParameterElement elem, int substitutionOffset) {
- CompletionItem item = new CompletionItem(elem.getSimpleName().toString());
- item.setKind(elementKind2CompletionItemKind(elem.getKind()));
- item.setSortText(String.format("%4d%s", 1700, elem.getSimpleName().toString())); //NOI18N
- return item;
- }
-
- @Override
- public CompletionItem createVariableItem(CompilationInfo info, VariableElement elem, TypeMirror type, int substitutionOffset, ReferencesCount referencesCount, boolean isInherited, boolean isDeprecated, boolean smartType, int assignToVarOffset) {
- CompletionItem item = new CompletionItem(elem.getSimpleName().toString());
- item.setKind(elementKind2CompletionItemKind(elem.getKind()));
- int priority = elem.getKind() == ElementKind.ENUM_CONSTANT || elem.getKind() == ElementKind.FIELD ? smartType ? 300 : 1300 : smartType ? 200 : 1200;
- item.setSortText(String.format("%4d%s", priority, elem.getSimpleName().toString())); //NOI18N
- setCompletionData(item, elem);
- return item;
- }
-
- @Override
- public CompletionItem createVariableItem(CompilationInfo info, String varName, int substitutionOffset, boolean newVarName, boolean smartType) {
- CompletionItem item = new CompletionItem(varName);
- item.setKind(CompletionItemKind.Variable);
- item.setSortText(String.format("%4d%s", smartType ? 200 : 1200, varName)); //NOI18N
- return item;
- }
-
- @Override
- public CompletionItem createExecutableItem(CompilationInfo info, ExecutableElement elem, ExecutableType type, int substitutionOffset, ReferencesCount referencesCount, boolean isInherited, boolean isDeprecated, boolean inImport, boolean addSemicolon, boolean smartType, int assignToVarOffset, boolean memberRef) {
- Iterator<? extends VariableElement> it = elem.getParameters().iterator();
- Iterator<? extends TypeMirror> tIt = type.getParameterTypes().iterator();
- StringBuilder label = new StringBuilder();
- String sep = "";
- label.append(elem.getSimpleName().toString());
- label.append("(");
- StringBuilder sortParams = new StringBuilder();
- sortParams.append('(');
- int cnt = 0;
- while(it.hasNext() && tIt.hasNext()) {
- TypeMirror tm = tIt.next();
- if (tm == null) {
- break;
- }
- label.append(sep);
- String paramTypeName = Utilities.getTypeName(info, tm, false, elem.isVarArgs() && !tIt.hasNext()).toString();
- label.append(paramTypeName);
- label.append(' ');
- label.append(it.next().getSimpleName().toString());
- sep = ", ";
- sortParams.append(paramTypeName);
- if (tIt.hasNext()) {
- sortParams.append(',');
- }
- cnt++;
- }
- label.append(") : ");
- sortParams.append(')');
- TypeMirror retType = type.getReturnType();
- label.append(Utilities.getTypeName(info, retType, false).toString());
- CompletionItem item = new CompletionItem(label.toString());
- item.setKind(elementKind2CompletionItemKind(elem.getKind()));
- StringBuilder insertText = new StringBuilder();
- insertText.append(elem.getSimpleName());
- insertText.append("(");
- if (elem.getParameters().isEmpty()) {
- insertText.append(")");
- }
- item.setInsertText(insertText.toString());
- item.setInsertTextFormat(InsertTextFormat.PlainText);
- int priority = elem.getKind() == ElementKind.METHOD ? smartType ? 500 : 1500 : smartType ? 650 : 1650;
- item.setSortText(String.format("%4d%s#%2d%s", priority, elem.getSimpleName().toString(), cnt, sortParams)); //NOI18N
- setCompletionData(item, elem);
- return item;
- }
-
- @Override
- public CompletionItem createThisOrSuperConstructorItem(CompilationInfo info, ExecutableElement elem, ExecutableType type, int substitutionOffset, boolean isDeprecated, String name) {
- CompletionItem item = createExecutableItem(info, elem, type, substitutionOffset, null, false, isDeprecated, false, false, false, -1, false);
- item.setLabel(name != null ? name : elem.getEnclosingElement().getSimpleName().toString());
- StringBuilder insertText = new StringBuilder();
- insertText.append(item.getLabel());
- insertText.append("(");
- if (elem.getParameters().isEmpty()) {
- insertText.append(")");
- }
- item.setInsertText(insertText.toString());
- item.setKind(CompletionItemKind.Constructor);
- item.setSortText(String.format("%4d%s", name != null ? 1550 : 1650, item.getSortText().substring(4))); //NOI18N
- setCompletionData(item, elem);
- return item;
- }
-
- @Override
- public CompletionItem createOverrideMethodItem(CompilationInfo info, ExecutableElement elem, ExecutableType type, int substitutionOffset, boolean implement) {
- CompletionItem item = createExecutableItem(info, elem, type, substitutionOffset, null, false, false, false, false, false, -1, false);
- item.setLabel(String.format("%s - %s", item.getLabel(), implement ? "implement" : "override"));
- item.setInsertText(null);
- item.setKind(elementKind2CompletionItemKind(elem.getKind()));
- try {
- List<TextEdit> textEdits = modify2TextEdits(JavaSource.forFileObject(info.getFileObject()), wc -> {
- wc.toPhase(JavaSource.Phase.ELEMENTS_RESOLVED);
- TreePath tp = wc.getTreeUtilities().pathFor(offset);
- if (implement) {
- GeneratorUtils.generateAbstractMethodImplementation(wc, tp, elem, offset);
- } else {
- GeneratorUtils.generateMethodOverride(wc, tp, elem, offset);
- }
- });
- if (!textEdits.isEmpty()) {
- item.setTextEdit(textEdits.get(0));
- }
- } catch (IOException ex) {
- //TODO: include stack trace:
- client.logMessage(new MessageParams(MessageType.Error, ex.getMessage()));
- }
- setCompletionData(item, elem);
- return item;
- }
-
- @Override
- public CompletionItem createGetterSetterMethodItem(CompilationInfo info, VariableElement elem, TypeMirror type, int substitutionOffset, String name, boolean setter) {
- return null; //TODO: fill
- }
-
- @Override
- public CompletionItem createDefaultConstructorItem(TypeElement elem, int substitutionOffset, boolean smartType) {
- return null; //TODO: fill
- }
-
- @Override
- public CompletionItem createParametersItem(CompilationInfo info, ExecutableElement elem, ExecutableType type, int substitutionOffset, boolean isDeprecated, int activeParamIndex, String name) {
- return null; //TODO: fill
- }
-
- @Override
- public CompletionItem createAnnotationItem(CompilationInfo info, TypeElement elem, DeclaredType type, int substitutionOffset, ReferencesCount referencesCount, boolean isDeprecated) {
- return null; //TODO: fill
- }
-
- @Override
- public CompletionItem createAttributeItem(CompilationInfo info, ExecutableElement elem, ExecutableType type, int substitutionOffset, boolean isDeprecated) {
- CompletionItem item = new CompletionItem(elem.getSimpleName().toString());
- item.setKind(CompletionItemKind.Property);
- StringBuilder insertText = new StringBuilder();
- insertText.append(elem.getSimpleName());
- insertText.append("=");
- item.setInsertText(insertText.toString());
- item.setInsertTextFormat(InsertTextFormat.PlainText);
- int priority = isDeprecated ? 100 + DEPRECATED : 100;
- item.setSortText(String.format("%4d%s", priority, elem.getSimpleName().toString()));
- setCompletionData(item, elem);
- return item;
- }
-
- @Override
- public CompletionItem createAttributeValueItem(CompilationInfo info, String value, String documentation, TypeElement element, int substitutionOffset, ReferencesCount referencesCount) {
- CompletionItem item = new CompletionItem(value);
- item.setKind(CompletionItemKind.Text);
- item.setSortText(value);
- item.setDocumentation(documentation);
- return item;
- }
-
- private static final Object KEY_IMPORT_TEXT_EDITS = new Object();
-
- @Override
- public CompletionItem createStaticMemberItem(CompilationInfo info, DeclaredType type, Element memberElem, TypeMirror memberType, boolean multipleVersions, int substitutionOffset, boolean isDeprecated, boolean addSemicolon) {
- //TODO: prefer static imports (but would be much slower?)
- //TODO: should be resolveImport instead of addImports:
- Map<Element, List<TextEdit>> imports = (Map<Element, List<TextEdit>>) info.getCachedValue(KEY_IMPORT_TEXT_EDITS);
- if (imports == null) {
- info.putCachedValue(KEY_IMPORT_TEXT_EDITS, imports = new HashMap<>(), CacheClearPolicy.ON_TASK_END);
- }
- List<TextEdit> currentClassImport = imports.computeIfAbsent(type.asElement(), toImport -> {
- try {
- return modify2TextEdits(JavaSource.forFileObject(info.getFileObject()), wc -> {
- wc.toPhase(JavaSource.Phase.ELEMENTS_RESOLVED);
- wc.rewrite(info.getCompilationUnit(), GeneratorUtilities.get(wc).addImports(wc.getCompilationUnit(), new HashSet<>(Arrays.asList(toImport))));
- });
- } catch (IOException ex) {
- //TODO: include stack trace:
- client.logMessage(new MessageParams(MessageType.Error, ex.getMessage()));
- return Collections.emptyList();
- }
- });
- String label = type.asElement().getSimpleName() + "." + memberElem.getSimpleName();
- CompletionItem item = new CompletionItem(label);
- item.setKind(elementKind2CompletionItemKind(memberElem.getKind()));
- item.setInsertText(label);
- item.setInsertTextFormat(InsertTextFormat.PlainText);
- item.setAdditionalTextEdits(currentClassImport);
- String sortText = memberElem.getSimpleName().toString();
- if (memberElem.getKind().isField()) {
- sortText += String.format("#%s", Utilities.getTypeName(info, type, false)); //NOI18N
- } else {
- StringBuilder sortParams = new StringBuilder();
- sortParams.append('(');
- int cnt = 0;
- Iterator<? extends TypeMirror> tIt = ((ExecutableType)memberType).getParameterTypes().iterator();
- while(tIt.hasNext()) {
- TypeMirror tm = tIt.next();
- if (tm == null) {
- break;
- }
- sortParams.append(Utilities.getTypeName(info, tm, false, ((ExecutableElement)memberElem).isVarArgs() && !tIt.hasNext()).toString());
- if (tIt.hasNext()) {
- sortParams.append(',');
- }
- cnt++;
- }
- sortParams.append(')');
- sortText += String.format("#%2d#%s#s", cnt, sortParams.toString(), Utilities.getTypeName(info, type, false)); //NOI18N
- }
- item.setSortText(String.format("%4d%s", memberElem.getKind().isField() ? 720 : 750, sortText)); //NOI18N
- setCompletionData(item, memberElem);
- return item;
- }
-
- @Override
- public CompletionItem createStaticMemberItem(ElementHandle<TypeElement> handle, String name, int substitutionOffset, boolean addSemicolon, ReferencesCount referencesCount, Source source) {
- return null; //TODO: fill
- }
-
- @Override
- public CompletionItem createChainedMembersItem(CompilationInfo info, List<? extends Element> chainedElems, List<? extends TypeMirror> chainedTypes, int substitutionOffset, boolean isDeprecated, boolean addSemicolon) {
- return null; //TODO: fill
- }
-
- @Override
- public CompletionItem createInitializeAllConstructorItem(CompilationInfo info, boolean isDefault, Iterable<? extends VariableElement> fields, ExecutableElement superConstructor, TypeElement parent, int substitutionOffset) {
- return null; //TODO: fill
- }
-
- private static CompletionItemKind elementKind2CompletionItemKind(ElementKind kind) {
- switch (kind) {
- case PACKAGE:
- return CompletionItemKind.Folder;
- case ENUM:
- return CompletionItemKind.Enum;
- case CLASS:
- return CompletionItemKind.Class;
- case ANNOTATION_TYPE:
- return CompletionItemKind.Interface;
- case INTERFACE:
- return CompletionItemKind.Interface;
- case ENUM_CONSTANT:
- return CompletionItemKind.EnumMember;
- case FIELD:
- return CompletionItemKind.Field;
- case PARAMETER:
- return CompletionItemKind.Variable;
- case LOCAL_VARIABLE:
- return CompletionItemKind.Variable;
- case EXCEPTION_PARAMETER:
- return CompletionItemKind.Variable;
- case METHOD:
- return CompletionItemKind.Method;
- case CONSTRUCTOR:
- return CompletionItemKind.Constructor;
- case TYPE_PARAMETER:
- return CompletionItemKind.TypeParameter;
- case RESOURCE_VARIABLE:
- return CompletionItemKind.Variable;
- case MODULE:
- return CompletionItemKind.Module;
- case STATIC_INIT:
- case INSTANCE_INIT:
- case OTHER:
- default:
- return CompletionItemKind.Text;
- }
- }
- }
-
private static final RequestProcessor JAVADOC_WORKER = new RequestProcessor(TextDocumentServiceImpl.class.getName() + ".javadoc", 1);
@Override
public CompletableFuture<CompletionItem> resolveCompletionItem(CompletionItem ci) {
JsonObject rawData = (JsonObject) ci.getData();
- if (rawData == null) {
- return CompletableFuture.completedFuture(ci);
- }
- CompletionData data = new Gson().fromJson(rawData, CompletionData.class);
- try {
- FileObject file = fromURI(data.uri);
- if (file == null) {
- return CompletableFuture.completedFuture(ci);
- }
- EditorCookie ec = file.getLookup().lookup(EditorCookie.class);
- Document doc = ec.openDocument();
- final ElementHandle<Element> handle = ElementHandleAccessor.getInstance().create(ElementKind.valueOf(data.kind), data.elementHandle);
- final JavaDocumentationTask<Future<String>> task = JavaDocumentationTask.create(-1, 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();
+ if (rawData != null) {
+ CompletionData data = new Gson().fromJson(rawData, CompletionData.class);
+ Completion completion = lastCompletions.get(data.index);
+ if (completion != null) {
+ FileObject file = fromURI(data.uri);
+ if (file == null) {
+ return CompletableFuture.completedFuture(ci);
}
- }, () -> false);
- LineMap[] lm = new LineMap[1];
- ModificationResult mr = ModificationResult.runModificationTask(Collections.singletonList(Source.create(doc)), new UserTask() {
- @Override
- public void run(ResultIterator resultIterator) throws Exception {
- task.run(resultIterator);
- if (ci.getDetail() != null) {
- final WorkingCopy copy = WorkingCopy.get(resultIterator.getParserResult(data.offset));
- 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)));
+ if (completion.getDetail() != null) {
+ try {
+ String detail = completion.getDetail().get();
+ if (detail != null) {
+ ci.setDetail(detail);
}
- lm[0] = copy.getCompilationUnit().getLineMap();
+ } catch (Exception ex) {
}
}
- });
- List<? extends ModificationResult.Difference> diffs = mr.getDifferences(file);
- if (diffs != null && !diffs.isEmpty()) {
- List<TextEdit> edits = new ArrayList<>();
- for (ModificationResult.Difference diff : diffs) {
- edits.add(new TextEdit(new Range(Utils.createPosition(lm[0], diff.getStartPosition().getOffset()),
- Utils.createPosition(lm[0], diff.getEndPosition().getOffset())), diff.getNewText()));
+ if (completion.getDocumentation() != null) {
+ try {
+ String documentation = completion.getDocumentation().get();
+ if (documentation != null) {
+ MarkupContent markup = new MarkupContent();
+ markup.setKind("markdown");
+ markup.setValue(html2MD(documentation));
+ ci.setDocumentation(markup);
+ }
+ } catch (Exception ex) {
+ }
}
- ci.setAdditionalTextEdits(edits);
- }
- Future<String> futureJavadoc = task.getDocumentation();
- CompletableFuture<CompletionItem> result = new CompletableFuture<CompletionItem>() {
- @Override
- public boolean cancel(boolean mayInterruptIfRunning) {
- return futureJavadoc.cancel(mayInterruptIfRunning) && super.cancel(mayInterruptIfRunning);
+ if (completion.getAdditionalTextEdits() != null) {
+ try {
+ List<org.netbeans.api.lsp.TextEdit> additionalTextEdits = completion.getAdditionalTextEdits().get();
+ if (additionalTextEdits != null) {
+ ci.setAdditionalTextEdits(additionalTextEdits.stream().map(ed -> {
+ return new TextEdit(new Range(Utils.createPosition(file, ed.getStartOffset()), Utils.createPosition(file, ed.getEndOffset())), ed.getNewText());
+ }).collect(Collectors.toList()));
+ }
+ } catch (Exception ex) {
+ }
}
- };
- JAVADOC_WORKER.post(() -> {
- try {
- String javadoc = futureJavadoc.get();
- MarkupContent markup = new MarkupContent();
- markup.setKind("markdown");
- markup.setValue(html2MD(javadoc));
- ci.setDocumentation(markup);
- result.complete(ci);
- } catch (ExecutionException | InterruptedException ex) {
- result.completeExceptionally(ex);
- }
- });
- return result;
- } catch (IOException | ParseException ex) {
- CompletableFuture<CompletionItem> result = new CompletableFuture<CompletionItem>();
- result.completeExceptionally(ex);
- return result;
+ }
}
+ return CompletableFuture.completedFuture(ci);
}
public static String html2MD(String html) {
@@ -864,30 +497,25 @@ public class TextDocumentServiceImpl implements TextDocumentService, LanguageCli
@Override
public CompletableFuture<Either<List<? extends Location>, List<? extends LocationLink>>> definition(DefinitionParams params) {
- String uri = params.getTextDocument().getUri();
- JavaSource js = getJavaSource(uri);
- if (js == null) {
- return CompletableFuture.completedFuture(Either.forLeft(new ArrayList<>()));
- }
- GoToTarget[] target = new GoToTarget[1];
- LineMap[] thisFileLineMap = new LineMap[1];
try {
- js.runUserActionTask(cc -> {
- cc.toPhase(JavaSource.Phase.RESOLVED);
- Document doc = cc.getSnapshot().getSource().getDocument(true);
- int offset = Utils.getOffset(doc, params.getPosition());
- Context context = GoToSupport.resolveContext(cc, doc, offset, false, false);
- if (context == null) {
- return ;
+ String uri = params.getTextDocument().getUri();
+ Document doc = openedDocuments.get(uri);
+ if (doc != null) {
+ FileObject file = Utils.fromUri(uri);
+ if (file != null) {
+ int offset = Utils.getOffset(doc, params.getPosition());
+ return HyperlinkLocation.resolve(doc, offset).thenApply(locs -> {
+ return Either.forLeft(locs.stream().map(location -> {
+ FileObject fo = location.getFileObject();
+ return new Location(Utils.toUri(fo), new Range(Utils.createPosition(fo, location.getStartOffset()), Utils.createPosition(fo, location.getEndOffset())));
+ }).collect(Collectors.toList()));
+ });
}
- target[0] = GoToSupport.computeGoToTarget(cc, context, offset);
- thisFileLineMap[0] = cc.getCompilationUnit().getLineMap();
- }, true);
- } catch (IOException ex) {
- //TODO: include stack trace:
+ }
+ } catch (MalformedURLException ex) {
client.logMessage(new MessageParams(MessageType.Error, ex.getMessage()));
}
- return gotoTarget2Location(uri, target[0], thisFileLineMap[0]).thenApply(location -> Either.forLeft(location != null ? Collections.singletonList(location) : Collections.emptyList()));
+ return CompletableFuture.completedFuture(Either.forLeft(Collections.emptyList()));
}
@Override
@@ -976,8 +604,9 @@ public class TextDocumentServiceImpl implements TextDocumentService, LanguageCli
@Override
public CompletableFuture<List<? extends Location>> references(ReferenceParams params) {
+ final Project[] projects = server.openedProjects().getNow(null);
// shortcut: if the projects are not yet initialized, return empty:
- if (server.openedProjects().getNow(null) == null) {
+ if (projects == null) {
return CompletableFuture.completedFuture(Collections.emptyList());
}
AtomicBoolean cancel = new AtomicBoolean();
@@ -1040,6 +669,16 @@ public class TextDocumentServiceImpl implements TextDocumentService, LanguageCli
query[0] = new WhereUsedQuery(Lookups.singleton(TreePathHandle.create(path, cc)));
}, true);
if (cancel.get()) return ;
+ List<FileObject> sourceRoots = new ArrayList<>();
+ for (Project project : projects) {
+ Sources sources = ProjectUtils.getSources(project);
+ for (SourceGroup sourceGroup : sources.getSourceGroups(JavaProjectConstants.SOURCES_TYPE_JAVA)) {
+ sourceRoots.add(sourceGroup.getRootFolder());
+ }
+ }
+ if (!sourceRoots.isEmpty()) {
+ query[0].getContext().add(org.netbeans.modules.refactoring.api.Scope.create(sourceRoots, null, null));
+ }
cancelCallback[0] = () -> query[0].cancelRequest();
RefactoringSession refactoring = RefactoringSession.create("FindUsages");
Problem p;
@@ -1209,7 +848,7 @@ public class TextDocumentServiceImpl implements TextDocumentService, LanguageCli
return CompletableFuture.completedFuture(Collections.emptyList());
}
JavaSource js = JavaSource.forDocument(doc);
- if (doc == null || js == null) {
+ if (js == null) {
return CompletableFuture.completedFuture(Collections.emptyList());
}
Map<String, ErrorDescription> id2Errors = (Map<String, ErrorDescription>) doc.getProperty("lsp-errors");
@@ -1844,42 +1483,44 @@ public class TextDocumentServiceImpl implements TextDocumentService, LanguageCli
GoToTarget[] target = new GoToTarget[1];
LineMap[] thisFileLineMap = new LineMap[1];
try {
- js.runUserActionTask(cc -> {
- cc.toPhase(JavaSource.Phase.RESOLVED);
- Document doc = cc.getSnapshot().getSource().getDocument(true);
- int offset = Utils.getOffset(doc, position);
- TreeUtilities treeUtilities = cc.getTreeUtilities();
- TreePath path = treeUtilities.getPathElementOfKind(Kind.METHOD, treeUtilities.pathFor(offset));
- if (path != null) {
- Trees trees = cc.getTrees();
- Element resolved = trees.getElement(path);
- if (resolved != null && resolved.getKind() == ElementKind.METHOD) {
- Map<ElementHandle<? extends Element>, List<ElementDescription>> overriding = new ComputeOverriding(new AtomicBoolean()).process(cc);
- List<ElementDescription> eds = overriding.get(ElementHandle.create(resolved));
- if (eds != null) {
- Iterator<ElementDescription> it = eds.iterator();
- if (it.hasNext()) {
- ElementDescription ed = it.next();
- Element el = ed.getHandle().resolve(cc);
- TreePath tp = trees.getPath(el);
- long startPos = tp != null && cc.getCompilationUnit() == tp.getCompilationUnit() ? trees.getSourcePositions().getStartPosition(cc.getCompilationUnit(), tp.getLeaf()) : -1;
- if (startPos >= 0) {
- long endPos = trees.getSourcePositions().getEndPosition(cc.getCompilationUnit(), tp.getLeaf());
- target[0] = new GoToTarget(cc.getSnapshot().getOriginalOffset((int) startPos),
- cc.getSnapshot().getOriginalOffset((int) endPos), GoToSupport.getNameSpan(tp.getLeaf(), treeUtilities),
- null, null, null, ed.getDisplayName(), true);
- } else {
- TypeElement te = el != null ? cc.getElementUtilities().outermostTypeElement(el) : null;
- target[0] = new GoToTarget(-1, -1, null, cc.getClasspathInfo(),ed.getHandle(),
- te != null ? te.getQualifiedName().toString().replace('.', '/') + ".class" : null,
- ed.getDisplayName(), true);
+ if (js != null) {
+ js.runUserActionTask(cc -> {
+ cc.toPhase(JavaSource.Phase.RESOLVED);
+ Document doc = cc.getSnapshot().getSource().getDocument(true);
+ int offset = Utils.getOffset(doc, position);
+ TreeUtilities treeUtilities = cc.getTreeUtilities();
+ TreePath path = treeUtilities.getPathElementOfKind(Kind.METHOD, treeUtilities.pathFor(offset));
+ if (path != null) {
+ Trees trees = cc.getTrees();
+ Element resolved = trees.getElement(path);
+ if (resolved != null && resolved.getKind() == ElementKind.METHOD) {
+ Map<ElementHandle<? extends Element>, List<ElementDescription>> overriding = new ComputeOverriding(new AtomicBoolean()).process(cc);
+ List<ElementDescription> eds = overriding.get(ElementHandle.create(resolved));
+ if (eds != null) {
+ Iterator<ElementDescription> it = eds.iterator();
+ if (it.hasNext()) {
+ ElementDescription ed = it.next();
+ Element el = ed.getHandle().resolve(cc);
+ TreePath tp = trees.getPath(el);
+ long startPos = tp != null && cc.getCompilationUnit() == tp.getCompilationUnit() ? trees.getSourcePositions().getStartPosition(cc.getCompilationUnit(), tp.getLeaf()) : -1;
+ if (startPos >= 0) {
+ long endPos = trees.getSourcePositions().getEndPosition(cc.getCompilationUnit(), tp.getLeaf());
+ target[0] = new GoToTarget(cc.getSnapshot().getOriginalOffset((int) startPos),
+ cc.getSnapshot().getOriginalOffset((int) endPos), GoToSupport.getNameSpan(tp.getLeaf(), treeUtilities),
+ null, null, null, ed.getDisplayName(), true);
+ } else {
+ TypeElement te = el != null ? cc.getElementUtilities().outermostTypeElement(el) : null;
+ target[0] = new GoToTarget(-1, -1, null, cc.getClasspathInfo(),ed.getHandle(),
+ te != null ? te.getQualifiedName().toString().replace('.', '/') + ".class" : null,
+ ed.getDisplayName(), true);
+ }
}
}
+ thisFileLineMap[0] = cc.getCompilationUnit().getLineMap();
}
- thisFileLineMap[0] = cc.getCompilationUnit().getLineMap();
}
- }
- }, true);
+ }, true);
+ }
} catch (IOException ex) {
client.logMessage(new MessageParams(MessageType.Error, ex.getMessage()));
}
@@ -1890,66 +1531,14 @@ public class TextDocumentServiceImpl implements TextDocumentService, LanguageCli
Location location = null;
if (target != null && target.success) {
if (target.offsetToOpen < 0) {
- Object[] openInfo = ElementOpenAccessor.getInstance().getOpenInfo(target.cpInfo, target.elementToOpen, new AtomicBoolean());
- if (openInfo == null && target.resourceName != null) {
- // try to attach sources
- final ClassPath cp = ClassPathSupport.createProxyClassPath(
- target.cpInfo.getClassPath(ClasspathInfo.PathKind.BOOT),
- target.cpInfo.getClassPath(ClasspathInfo.PathKind.COMPILE),
- target.cpInfo.getClassPath(ClasspathInfo.PathKind.SOURCE));
- final FileObject resource = cp.findResource(target.resourceName);
- if (resource != null) {
- final FileObject root = cp.findOwnerRoot(resource);
- if (root != null) {
- final CompletableFuture<Location> future = new CompletableFuture<>();
- SourceJavadocAttacher.attachSources(root.toURL(), new SourceJavadocAttacher.AttachmentListener() {
- @Override
- public void attachmentSucceeded() {
- Object[] openInfo = ElementOpenAccessor.getInstance().getOpenInfo(target.cpInfo, target.elementToOpen, new AtomicBoolean());
- if (openInfo != null && (int) openInfo[1] != (-1) && (int) openInfo[2] != (-1) && openInfo[3] != null) {
- future.complete(openInfo2Location(openInfo));
- } else {
- attachmentFailed();
- }
- }
-
- @Override
- public void attachmentFailed() {
- try {
- FileObject generated = org.netbeans.modules.java.classfile.CodeGenerator.generateCode(target.cpInfo, target.elementToOpen);
- if (generated != null) {
- final int[] pos = new int[] {-1};
- try {
- JavaSource.create(target.cpInfo, generated).runUserActionTask(new Task<CompilationController>() {
- @Override public void run(CompilationController parameter) throws Exception {
- parameter.toPhase(JavaSource.Phase.RESOLVED);
- Element el = target.elementToOpen.resolve(parameter);
- if (el != null) {
- TreePath p = parameter.getTrees().getPath(el);
- if (p != null) {
- pos[0] = (int) parameter.getTrees().getSourcePositions().getStartPosition(p.getCompilationUnit(), p.getLeaf());
- }
- }
- }
- }, true);
- } catch (IOException ex) {
- }
- int offset = pos[0] != -1 ? pos[0] : 0;
- future.complete(new Location(Utils.toUri(generated), new Range(Utils.createPosition(generated, offset), Utils.createPosition(generated, offset))));
- return;
- }
- } catch (Exception e) {
- }
- future.complete(null);
- }
- });
- return future;
- }
+ final CompletableFuture<HyperlinkLocation> future = ElementOpen.getLocation(target.cpInfo, target.elementToOpen, target.resourceName);
+ return future.thenApply(loc -> {
+ if (loc != null) {
+ FileObject fo = loc.getFileObject();
+ return new Location(Utils.toUri(fo), new Range(Utils.createPosition(fo, loc.getStartOffset()), Utils.createPosition(fo, loc.getEndOffset())));
}
- }
- if (openInfo != null && (int) openInfo[1] != (-1) && (int) openInfo[2] != (-1) && openInfo[3] != null) {
- location = openInfo2Location(openInfo);
- }
+ return null;
+ });
} else {
int start = target.nameSpan != null ? target.nameSpan[0] : target.offsetToOpen;
int end = target.nameSpan != null ? target.nameSpan[1] : target.endPos;
@@ -1959,20 +1548,6 @@ public class TextDocumentServiceImpl implements TextDocumentService, LanguageCli
return CompletableFuture.completedFuture(location);
}
- private Location openInfo2Location(Object[] openInfo) {
- FileObject file = (FileObject) openInfo[0];
- int start = (int) openInfo[3];
- if (start < 0) {
- start = (int) openInfo[1];
- }
- int end = (int) openInfo[4];
- if (end < 0) {
- end = (int) openInfo[2];
- }
- LineMap lm = (LineMap) openInfo[5];
- return new Location(Utils.toUri(file), new Range(Utils.createPosition(lm, start), Utils.createPosition(lm, end)));
- }
-
private void runDiagnoticTasks(String uri) {
if (server.openedProjects().getNow(null) == null) {
return;
@@ -2015,47 +1590,49 @@ public class TextDocumentServiceImpl implements TextDocumentService, LanguageCli
@Override
public void run(ResultIterator it) throws Exception {
CompilationController cc = CompilationController.get(it.getParserResult());
- cc.toPhase(JavaSource.Phase.RESOLVED);
- Map<String, ErrorDescription> id2Errors = new HashMap<>();
- List<Diagnostic> diags = new ArrayList<>();
- int idx = 0;
- List<ErrorDescription> errors = produceErrors.computeErrors(cc, doc);
- if (errors == null) {
- errors = Collections.emptyList();
- }
- for (ErrorDescription err : errors) {
- Diagnostic diag = new Diagnostic(new Range(Utils.createPosition(cc.getCompilationUnit(), err.getRange().getBegin().getOffset()),
- Utils.createPosition(cc.getCompilationUnit(), err.getRange().getEnd().getOffset())),
- err.getDescription());
- switch (err.getSeverity()) {
- case ERROR: diag.setSeverity(DiagnosticSeverity.Error); break;
- case VERIFIER:
- case WARNING: diag.setSeverity(DiagnosticSeverity.Warning); break;
- case HINT: diag.setSeverity(DiagnosticSeverity.Hint); break;
- default: diag.setSeverity(DiagnosticSeverity.Information); break;
+ if (cc != null) {
+ cc.toPhase(JavaSource.Phase.RESOLVED);
+ Map<String, ErrorDescription> id2Errors = new HashMap<>();
+ List<Diagnostic> diags = new ArrayList<>();
+ int idx = 0;
+ List<ErrorDescription> errors = produceErrors.computeErrors(cc, doc);
+ if (errors == null) {
+ errors = Collections.emptyList();
}
- String id = keyPrefix + ":" + idx++ + "-" + err.getId();
- diag.setCode(id);
- id2Errors.put(id, err);
- diags.add(diag);
- }
- doc.putProperty("lsp-errors-" + keyPrefix, id2Errors);
- doc.putProperty("lsp-errors-diags-" + keyPrefix, diags);
- Map<String, ErrorDescription> mergedId2Errors = new HashMap<>();
- List<Diagnostic> mergedDiags = new ArrayList<>();
- for (String k : ERROR_KEYS) {
- Map<String, ErrorDescription> prevErrors = (Map<String, ErrorDescription>) doc.getProperty("lsp-errors-" + k);
- if (prevErrors != null) {
- mergedId2Errors.putAll(prevErrors);
+ for (ErrorDescription err : errors) {
+ Diagnostic diag = new Diagnostic(new Range(Utils.createPosition(cc.getCompilationUnit(), err.getRange().getBegin().getOffset()),
+ Utils.createPosition(cc.getCompilationUnit(), err.getRange().getEnd().getOffset())),
+ err.getDescription());
+ switch (err.getSeverity()) {
+ case ERROR: diag.setSeverity(DiagnosticSeverity.Error); break;
+ case VERIFIER:
+ case WARNING: diag.setSeverity(DiagnosticSeverity.Warning); break;
+ case HINT: diag.setSeverity(DiagnosticSeverity.Hint); break;
+ default: diag.setSeverity(DiagnosticSeverity.Information); break;
+ }
+ String id = keyPrefix + ":" + idx++ + "-" + err.getId();
+ diag.setCode(id);
+ id2Errors.put(id, err);
+ diags.add(diag);
}
- List<Diagnostic> prevDiags = (List<Diagnostic>) doc.getProperty("lsp-errors-diags-" + k);
- if (prevDiags != null) {
- mergedDiags.addAll(prevDiags);
+ doc.putProperty("lsp-errors-" + keyPrefix, id2Errors);
+ doc.putProperty("lsp-errors-diags-" + keyPrefix, diags);
+ Map<String, ErrorDescription> mergedId2Errors = new HashMap<>();
+ List<Diagnostic> mergedDiags = new ArrayList<>();
+ for (String k : ERROR_KEYS) {
+ Map<String, ErrorDescription> prevErrors = (Map<String, ErrorDescription>) doc.getProperty("lsp-errors-" + k);
+ if (prevErrors != null) {
+ mergedId2Errors.putAll(prevErrors);
+ }
+ List<Diagnostic> prevDiags = (List<Diagnostic>) doc.getProperty("lsp-errors-diags-" + k);
+ if (prevDiags != null) {
+ mergedDiags.addAll(prevDiags);
+ }
}
+ doc.putProperty("lsp-errors", mergedId2Errors);
+ doc.putProperty("lsp-errors-diags", mergedDiags);
+ publishDiagnostics(uri, mergedDiags);
}
- doc.putProperty("lsp-errors", mergedId2Errors);
- doc.putProperty("lsp-errors-diags", mergedDiags);
- publishDiagnostics(uri, mergedDiags);
}
});
} catch (IOException | ParseException ex) {
diff --git a/java/java.lsp.server/vscode/src/extension.ts b/java/java.lsp.server/vscode/src/extension.ts
index 86e6e25..a506037 100644
--- a/java/java.lsp.server/vscode/src/extension.ts
+++ b/java/java.lsp.server/vscode/src/extension.ts
@@ -465,7 +465,7 @@ function doActivateWithJDK(specifiedJDK: string | null, context: ExtensionContex
// Options to control the language client
let clientOptions: LanguageClientOptions = {
// Register the server for java documents
- documentSelector: ['java'],
+ documentSelector: [{ language: 'java' }, { language: 'yaml', pattern: '**/application.yml' }],
synchronize: {
configurationSection: 'java',
fileEvents: [
diff --git a/java/java.sourceui/apichanges.xml b/java/java.sourceui/apichanges.xml
index 20fd173..16f8927 100644
--- a/java/java.sourceui/apichanges.xml
+++ b/java/java.sourceui/apichanges.xml
@@ -84,6 +84,18 @@ is the proper place.
<!-- ACTUAL CHANGES BEGIN HERE: -->
<changes>
+ <change id="ElementOpenGetLocation">
+ <api name="general"/>
+ <summary>Adding ElementOpen.getLocation(ClasspathInfo, ElementHandle, String)</summary>
+ <version major="1" minor="45"/>
+ <date day="25" month="3" year="2021"/>
+ <author login="dbalek"/>
+ <compatibility addition="yes">
+ </compatibility>
+ <description>
+ Adding ElementOpen.getLocation(ClasspathInfo, ElementHandle, String) method.
+ </description>
+ </change>
<change id="ElementOpenForTreePathHandle">
<api name="general"/>
<summary>Adding ElementOpen.open(FileObject toSearch, TreePathHandle toOpen)</summary>
diff --git a/java/java.sourceui/nbproject/project.properties b/java/java.sourceui/nbproject/project.properties
index 01b9ec4..1f40101 100644
--- a/java/java.sourceui/nbproject/project.properties
+++ b/java/java.sourceui/nbproject/project.properties
@@ -18,7 +18,7 @@ javadoc.apichanges=${basedir}/apichanges.xml
javac.compilerargs=-Xlint -Xlint:-serial
javac.source=1.8
javadoc.arch=${basedir}/arch.xml
-spec.version.base=1.57.0
+spec.version.base=1.58.0
test.config.stableBTD.includes=**/*Test.class
test.config.stableBTD.excludes=\
diff --git a/java/java.sourceui/nbproject/project.xml b/java/java.sourceui/nbproject/project.xml
index 33f9b27..9558ec0 100644
--- a/java/java.sourceui/nbproject/project.xml
+++ b/java/java.sourceui/nbproject/project.xml
@@ -105,6 +105,15 @@
</run-dependency>
</dependency>
<dependency>
+ <code-name-base>org.netbeans.modules.editor.lib</code-name-base>
+ <build-prerequisite/>
+ <compile-dependency/>
+ <run-dependency>
+ <release-version>3</release-version>
+ <specification-version>4.19</specification-version>
+ </run-dependency>
+ </dependency>
+ <dependency>
<code-name-base>org.netbeans.modules.editor.lib2</code-name-base>
<build-prerequisite/>
<compile-dependency/>
diff --git a/java/java.sourceui/src/org/netbeans/api/java/source/ui/ElementOpen.java b/java/java.sourceui/src/org/netbeans/api/java/source/ui/ElementOpen.java
index 61db694..2e03553 100644
--- a/java/java.sourceui/src/org/netbeans/api/java/source/ui/ElementOpen.java
+++ b/java/java.sourceui/src/org/netbeans/api/java/source/ui/ElementOpen.java
@@ -23,6 +23,7 @@ import com.sun.source.util.SourcePositions;
import com.sun.source.util.TreePath;
import org.netbeans.api.java.source.support.ErrorAwareTreePathScanner;
import java.io.IOException;
+import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -38,13 +39,16 @@ import org.netbeans.api.editor.fold.FoldHierarchy;
import org.netbeans.api.editor.fold.FoldUtilities;
import org.netbeans.api.java.classpath.ClassPath;
import org.netbeans.api.java.platform.JavaPlatform;
+import org.netbeans.api.java.queries.SourceJavadocAttacher;
import org.netbeans.api.java.source.*;
import org.netbeans.api.progress.ProgressUtils;
import org.netbeans.modules.java.BinaryElementOpen;
+import org.netbeans.modules.java.classfile.CodeGenerator;
import org.netbeans.modules.java.source.JavaSourceAccessor;
import org.netbeans.modules.java.source.parsing.ClassParser;
import org.netbeans.modules.java.source.parsing.FileObjects;
import org.netbeans.modules.java.source.ui.ElementOpenAccessor;
+import org.netbeans.spi.java.classpath.support.ClassPathSupport;
import org.openide.awt.StatusDisplayer;
import org.openide.cookies.EditorCookie;
import org.openide.filesystems.FileObject;
@@ -276,12 +280,127 @@ public final class ElementOpen {
}
}
+ /**
+ * Gets location of the {@link Element} corresponding to the given {@link ElementHandle}.
+ *
+ * @param cpInfo ClasspathInfo which should be used for the search
+ * @param el ElementHandle to search
+ * @param resourceName optional resource name to search
+ * @return location of the given element
+ *
+ * @since 1.58
+ */
+ public static CompletableFuture<Location> getLocation(final ClasspathInfo cpInfo, final ElementHandle<? extends Element> el, String resourceName) {
+ final CompletableFuture<Object[]> future = getFutureOpenInfo(cpInfo, el, resourceName, new AtomicBoolean());
+ return future.thenApply(openInfo -> {
+ if (openInfo[0] != null && (int) openInfo[1] != (-1) && (int) openInfo[2] != (-1)) {
+ FileObject file = (FileObject) openInfo[0];
+ int start = (int) openInfo[3];
+ if (start < 0) {
+ start = (int) openInfo[1];
+ }
+ int end = (int) openInfo[4];
+ if (end < 0) {
+ end = (int) openInfo[2];
+ }
+ return new Location(file, start, end);
+ }
+ return null;
+ });
+ }
+
+ /**
+ * Represents the location of an element. It is a range inside of a file object.
+ */
+ public static final class Location {
+
+ private final FileObject fileObject;
+ private final int startOffset;
+ private final int endOffset;
+
+ private Location(FileObject fileObject, int startOffset, int endOffset) {
+ this.fileObject = fileObject;
+ this.startOffset = startOffset;
+ this.endOffset = endOffset;
+ }
+
+ /**
+ * The location's file object.
+ *
+ * @return file object
+ */
+ public FileObject getFileObject() {
+ return fileObject;
+ }
+
+ /**
+ * The location's start offset.
+ *
+ * @return offset
+ */
+ public int getStartOffset() {
+ return startOffset;
+ }
+
+ /**
+ * The location's end offset.
+ *
+ * @return offset
+ */
+ public int getEndOffset() {
+ return endOffset;
+ }
+ }
// Private methods ---------------------------------------------------------
private static boolean isClassFile(@NonNull final FileObject file) {
return FileObjects.CLASS.equals(file.getExt()) || ClassParser.MIME_TYPE.equals(file.getMIMEType(ClassParser.MIME_TYPE));
}
+ private static CompletableFuture<Object[]> getFutureOpenInfo(final ClasspathInfo cpInfo, final ElementHandle<? extends Element> el, String resourceName, AtomicBoolean cancel) {
+ Object[] openInfo = getOpenInfo(cpInfo, el, cancel);
+ if (openInfo == null && resourceName != null) {
+ // try to attach sources
+ final ClassPath cp = ClassPathSupport.createProxyClassPath(
+ cpInfo.getClassPath(ClasspathInfo.PathKind.BOOT),
+ cpInfo.getClassPath(ClasspathInfo.PathKind.COMPILE),
+ cpInfo.getClassPath(ClasspathInfo.PathKind.SOURCE));
+ final FileObject resource = cp.findResource(resourceName);
+ if (resource != null) {
+ final FileObject root = cp.findOwnerRoot(resource);
+ if (root != null) {
+ final CompletableFuture<Object[]> future = new CompletableFuture<>();
+ SourceJavadocAttacher.attachSources(root.toURL(), new SourceJavadocAttacher.AttachmentListener() {
+ @Override
+ public void attachmentSucceeded() {
+ Object[] openInfo = getOpenInfo(cpInfo, el, cancel);
+ if (openInfo != null && (int) openInfo[1] != (-1) && (int) openInfo[2] != (-1) && openInfo[5] != null) {
+ future.complete(openInfo);
+ } else {
+ attachmentFailed();
+ }
+ }
+
+ @Override
+ public void attachmentFailed() {
+ try {
+ FileObject generated = CodeGenerator.generateCode(cpInfo, el);
+ if (generated != null) {
+ future.complete(getOpenInfo(generated, el, cancel));
+ return;
+ }
+ } catch (Exception e) {
+ }
+ future.complete(null);
+ }
+ });
+ return future;
+ }
+ }
+ }
+ return CompletableFuture.completedFuture(openInfo);
+ }
+
private static Object[] getOpenInfo(final ClasspathInfo cpInfo, final ElementHandle<? extends Element> el, AtomicBoolean cancel) {
FileObject fo = SourceUtils.getFile(el, cpInfo);
if (fo != null && fo.isFolder()) {
diff --git a/java/refactoring.java/src/org/netbeans/modules/refactoring/java/plugins/JavaWhereUsedQueryPlugin.java b/java/refactoring.java/src/org/netbeans/modules/refactoring/java/plugins/JavaWhereUsedQueryPlugin.java
index e1b7ec1..c617451 100644
--- a/java/refactoring.java/src/org/netbeans/modules/refactoring/java/plugins/JavaWhereUsedQueryPlugin.java
+++ b/java/refactoring.java/src/org/netbeans/modules/refactoring/java/plugins/JavaWhereUsedQueryPlugin.java
@@ -148,7 +148,9 @@ public class JavaWhereUsedQueryPlugin extends JavaRefactoringPlugin implements F
final Set<String> packageSet = new HashSet<>(packages.size());
for (NonRecursiveFolder nonRecursiveFolder : packages) {
String resourceName = cpInfo.getClassPath(ClasspathInfo.PathKind.SOURCE).getResourceName(nonRecursiveFolder.getFolder());
- packageSet.add(resourceName.replace('/', '.'));
+ if (resourceName != null) {
+ packageSet.add(resourceName.replace('/', '.'));
+ }
}
searchScopeType.add(new SearchScopeType() {
@Override
diff --git a/nbbuild/antsrc/org/netbeans/nbbuild/extlibs/ignored-overlaps b/nbbuild/antsrc/org/netbeans/nbbuild/extlibs/ignored-overlaps
index b952575..3ba36ae 100644
--- a/nbbuild/antsrc/org/netbeans/nbbuild/extlibs/ignored-overlaps
+++ b/nbbuild/antsrc/org/netbeans/nbbuild/extlibs/ignored-overlaps
@@ -82,9 +82,6 @@ enterprise/javaee7.api/external/javax.annotation-api-1.2.jar enterprise/websvc.r
enterprise/javaee7.api/external/jaxws-api-2.2.8.jar java/websvc.jaxws21api/external/jaxws-2.2.6-api.zip
enterprise/javaee7.api/external/jsr181-api-1.0-MR1.jar java/websvc.jaxws21api/external/jaxws-2.2.6-api.zip
-# the jcodings is used internally in two unrelated modules:
-ide/libs.bytelist/external/jcodings-1.0.18.jar ide/textmate.lexer/external/jcodings-1.0.18.jar
-
# ide/lsp.client and java/java.lsp.server both use LSP libraries, but are independent:
ide/lsp.client/external/org.eclipse.lsp4j-0.9.0.jar java/java.lsp.server/external/org.eclipse.lsp4j-0.9.0.jar
ide/lsp.client/external/org.eclipse.lsp4j.generator-0.9.0.jar java/java.lsp.server/external/org.eclipse.lsp4j.generator-0.9.0.jar
diff --git a/nbbuild/cluster.properties b/nbbuild/cluster.properties
index b5aee47..4d447b1 100644
--- a/nbbuild/cluster.properties
+++ b/nbbuild/cluster.properties
@@ -311,6 +311,7 @@ nb.cluster.ide.depends=\
nb.cluster.ide=\
api.debugger,\
api.java.classpath,\
+ api.lsp,\
api.xml,\
api.xml.ui,\
bcpg,\
@@ -438,6 +439,7 @@ nb.cluster.ide=\
libs.graalsdk,\
libs.ini4j,\
libs.jaxb,\
+ libs.jcodings,\
libs.jsch.agentproxy,\
libs.json_simple,\
libs.jvyamlb,\
@@ -821,6 +823,7 @@ nb.cluster.enterprise=\
libs.jstl,\
maven.j2ee,\
maven.jaxws,\
+ micronaut,\
payara.common,\
payara.eecommon,\
payara.jakartaee,\
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@netbeans.apache.org
For additional commands, e-mail: commits-help@netbeans.apache.org
For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists