Skip to content

Commit

Permalink
Enable Micronaut HyperlinkProviders - deadlocks fixed. (#5991)
Browse files Browse the repository at this point in the history
  • Loading branch information
dbalek authored May 29, 2023
1 parent 711207c commit 836fea4
Show file tree
Hide file tree
Showing 6 changed files with 179 additions and 40 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Stack;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.swing.text.Document;
Expand Down Expand Up @@ -58,6 +60,9 @@ public class MicronautConfigUtilities {

private static final Pattern REGEXP = Pattern.compile("^(application|bootstrap)(-\\w*)*\\.(yml|properties)$", Pattern.CASE_INSENSITIVE);

public static final String YAML_MIME = "text/x-yaml";
public static final String PROPERTIES_MIME = "text/x-properties";

public static boolean isMicronautConfigFile(FileObject fo) {
return fo != null && REGEXP.matcher(fo.getNameExt()).matches();
}
Expand All @@ -73,7 +78,7 @@ public static ConfigurationMetadataProperty resolveProperty(Document doc, int of
try {
int lineStart = LineDocumentUtils.getLineStart(lineDocument, offset);
String mimeType = DocumentUtilities.getMimeType(doc);
if ("text/x-yaml".equals(mimeType)) {
if (YAML_MIME.equals(mimeType)) {
String text = lineDocument.getText(lineStart, offset - lineStart);
if (!text.startsWith("#")) {
int idx = text.indexOf(':');
Expand All @@ -98,26 +103,7 @@ public static ConfigurationMetadataProperty resolveProperty(Document doc, int of
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 (groupKey.endsWith(".*")) {
groupKey = groupKey.substring(0, groupKey.length() - 2);
}
if (Pattern.matches(groupKey.replaceAll("\\.", "\\\\.").replaceAll("\\*", "\\\\w*") + ".*", propertyName)) {
ConfigurationMetadataGroup group = groupEntry.getValue();
if (sources != null) {
sources.addAll(group.getSources().values());
}
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();
return;
}
}
}
}
property[0] = getProperty(MicronautConfigProperties.getGroups(project), getPropertyName(context), sources);
}
}
}
Expand Down Expand Up @@ -180,7 +166,7 @@ public static ConfigurationMetadataProperty resolveProperty(Document doc, int of
public static void collectUsages(FileObject fo, String propertyName, Consumer<Usage> consumer) {
try {
String mimeType = fo.getMIMEType();
if ("text/x-yaml".equals(mimeType)) {
if (YAML_MIME.equals(mimeType)) {
ParserManager.parse(Collections.singleton(Source.create(fo)), new UserTask() {
public @Override void run(ResultIterator resultIterator) throws Exception {
Parser.Result r = resultIterator.getParserResult();
Expand Down Expand Up @@ -211,6 +197,70 @@ public static void collectUsages(FileObject fo, String propertyName, Consumer<Us
Exceptions.printStackTrace(ex);
}
}

public static List<int[]> getPropertySpans(Project project, Parser.Result r) {
List<int[]> spans = new ArrayList<>();
if (r instanceof ParserResult) {
Language language = LanguageRegistry.getInstance().getLanguageByMimeType(r.getSnapshot().getMimeType());
if (language != null) {
StructureScanner scanner = language.getStructure();
if (scanner != null) {
Map<String, ConfigurationMetadataGroup> groups = MicronautConfigProperties.getGroups(project);
scan(scanner.scan((ParserResult) r), new Stack<>(), context -> {
if (!context.empty()) {
String propertyName = getPropertyName(context);
List<ConfigurationMetadataSource> sources = new ArrayList<>();
ConfigurationMetadataProperty property = getProperty(groups, propertyName, sources);
if (property != null || !sources.isEmpty()) {
StructureItem item = context.peek();
spans.add(new int[] {(int) item.getPosition(), (int) item.getPosition() + item.getName().length()});
}
}
return true;
});
}
}
}
return spans;
}

private static ConfigurationMetadataProperty getProperty(Map<String, ConfigurationMetadataGroup> groups, String propertyName, List<ConfigurationMetadataSource> sources) {
for (Map.Entry<String, ConfigurationMetadataGroup> groupEntry : groups.entrySet()) {
String groupKey = groupEntry.getKey();
if (groupKey.endsWith(".*")) {
groupKey = groupKey.substring(0, groupKey.length() - 2);
}
if (Pattern.matches(groupKey.replaceAll("\\.", "\\\\.").replaceAll("\\*", "\\\\w*") + ".*", propertyName)) {
ConfigurationMetadataGroup group = groupEntry.getValue();
if (sources != null) {
sources.addAll(group.getSources().values());
}
for (Map.Entry<String, ConfigurationMetadataProperty> propertyEntry : group.getProperties().entrySet()) {
String propertyKey = propertyEntry.getKey();
if (Pattern.matches(propertyKey.replaceAll("\\.", "\\\\.").replaceAll("\\*", "\\\\w*"), propertyName)) {
return propertyEntry.getValue();
}
}
}
}
return null;
}

private static void scan(List<? extends StructureItem> structures, Stack<StructureItem> context, Function<Stack<StructureItem>, Boolean> visitor) {
for (StructureItem structure : structures) {
if (structure != null) {
try {
context.push(structure);
if (visitor.apply(context)) {
scan(structure.getNestedItems(), context, visitor);
}
} finally {
context.pop();
}
}
}
}


private static void find(FileObject fo, String propertyName, List<? extends StructureItem> structures, CharSequence content, Consumer<Usage> consumer) {
int idx = propertyName.indexOf('.');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,12 @@
*/
public class MicronautConfigCompletionCollector implements CompletionCollector {

@MimeRegistration(mimeType = "text/x-yaml", service = CompletionCollector.class)
@MimeRegistration(mimeType = MicronautConfigUtilities.YAML_MIME, service = CompletionCollector.class)
public static MicronautConfigCompletionCollector createYamlCollector() {
return new MicronautConfigCompletionCollector();
}

@MimeRegistration(mimeType = "text/x-properties", service = CompletionCollector.class)
@MimeRegistration(mimeType = MicronautConfigUtilities.PROPERTIES_MIME, service = CompletionCollector.class)
public static MicronautConfigCompletionCollector createPropertiesCollector() {
return new MicronautConfigCompletionCollector();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,12 @@ public class MicronautConfigCompletionProvider implements CompletionProvider {

public static final String PROPERTY_NAME_COLOR = getHTMLColor(64, 64, 217);

@MimeRegistration(mimeType = "text/x-yaml", service = CompletionProvider.class)
@MimeRegistration(mimeType = MicronautConfigUtilities.YAML_MIME, service = CompletionProvider.class)
public static MicronautConfigCompletionProvider createYamlProvider() {
return new MicronautConfigCompletionProvider();
}

@MimeRegistration(mimeType = "text/x-properties", service = CompletionProvider.class)
@MimeRegistration(mimeType = MicronautConfigUtilities.PROPERTIES_MIME, service = CompletionProvider.class)
public static MicronautConfigCompletionProvider createPropertiesProvider() {
return new MicronautConfigCompletionProvider();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
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.micronaut.MicronautConfigUtilities;
import org.netbeans.modules.parsing.api.ParserManager;
import org.netbeans.modules.parsing.api.ResultIterator;
import org.netbeans.modules.parsing.api.Source;
Expand Down Expand Up @@ -78,7 +79,7 @@ public <T> List<T> query(Document doc, int caretOffset, Project project, ItemFac
try {
String text = lineDocument.getText(lineStart, caretOffset - lineStart);
String mimeType = DocumentUtilities.getMimeType(doc);
if ("text/x-yaml".equals(mimeType)) {
if (MicronautConfigUtilities.YAML_MIME.equals(mimeType)) {
if (!text.startsWith("#")) {
int idx = text.indexOf(':');
if (idx < 0) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,12 @@
*/
public class MicronautHoverProvider implements HoverProvider {

@MimeRegistration(mimeType = "text/x-yaml", service = HoverProvider.class)
@MimeRegistration(mimeType = MicronautConfigUtilities.YAML_MIME, service = HoverProvider.class)
public static MicronautHoverProvider createYamlProvider() {
return new MicronautHoverProvider();
}

@MimeRegistration(mimeType = "text/x-properties", service = HoverProvider.class)
@MimeRegistration(mimeType = MicronautConfigUtilities.PROPERTIES_MIME, service = HoverProvider.class)
public static MicronautHoverProvider createPropertiesProvider() {
return new MicronautHoverProvider();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
import java.awt.Toolkit;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
Expand All @@ -42,28 +44,41 @@
import org.netbeans.api.java.source.ui.ElementOpen;
import org.netbeans.api.lsp.HyperlinkLocation;
import org.netbeans.api.progress.BaseProgressUtils;
import org.netbeans.api.project.FileOwnerQuery;
import org.netbeans.api.project.Project;
import org.netbeans.lib.editor.hyperlink.spi.HyperlinkProviderExt;
import org.netbeans.lib.editor.hyperlink.spi.HyperlinkType;
import org.netbeans.lib.editor.util.swing.DocumentUtilities;
import org.netbeans.modules.micronaut.MicronautConfigProperties;
import org.netbeans.modules.micronaut.MicronautConfigUtilities;
import org.netbeans.modules.parsing.api.Snapshot;
import org.netbeans.modules.parsing.spi.IndexingAwareParserResultTask;
import org.netbeans.modules.parsing.spi.Parser;
import org.netbeans.modules.parsing.spi.Scheduler;
import org.netbeans.modules.parsing.spi.SchedulerEvent;
import org.netbeans.modules.parsing.spi.SchedulerTask;
import org.netbeans.modules.parsing.spi.TaskFactory;
import org.netbeans.modules.parsing.spi.TaskIndexingMode;
import org.netbeans.spi.lsp.HyperlinkLocationProvider;
import org.openide.filesystems.FileObject;
import org.openide.util.NbBundle;
import org.springframework.boot.configurationmetadata.ConfigurationMetadataProperty;
import org.springframework.boot.configurationmetadata.ConfigurationMetadataSource;

/**
* CURRENTLY NOT ACTIVE - @MimeRegistration DISABLED to work around
* <a href="https://github.com/apache/netbeans/issues/3913">GITHUB-3913</a>
*
*
* @author Dusan Balek
*/
public class MicronautConfigHyperlinkProvider implements HyperlinkProviderExt {

//@MimeRegistration(mimeType = "text/x-yaml", service = HyperlinkProviderExt.class, position = 1250)
private static final String SPANS_PROPERTY_NAME = "MicronautConfigHyperlinkSpans";

@MimeRegistration(mimeType = MicronautConfigUtilities.YAML_MIME, service = HyperlinkProviderExt.class, position = 1250)
public static MicronautConfigHyperlinkProvider createYamlProvider() {
return new MicronautConfigHyperlinkProvider();
}

//@MimeRegistration(mimeType = "text/x-properties", service = HyperlinkProviderExt.class, position = 1250)
@MimeRegistration(mimeType = MicronautConfigUtilities.PROPERTIES_MIME, service = HyperlinkProviderExt.class, position = 1250)
public static MicronautConfigHyperlinkProvider createPropertiesProvider() {
return new MicronautConfigHyperlinkProvider();
}
Expand All @@ -80,10 +95,26 @@ public boolean isHyperlinkPoint(Document doc, int offset, HyperlinkType type) {

@Override
public int[] getHyperlinkSpan(Document doc, int offset, HyperlinkType type) {
int[] span = new int[2];
List<ConfigurationMetadataSource> sources = new ArrayList<>();
ConfigurationMetadataProperty property = MicronautConfigUtilities.resolveProperty(doc, offset, span, sources);
return property != null || !sources.isEmpty() ? span : null;
String mimeType = DocumentUtilities.getMimeType(doc);
if (MicronautConfigUtilities.YAML_MIME.equals(mimeType)) {
List<int[]> spans = null;
synchronized (doc) {
spans = (List<int[]>) doc.getProperty(SPANS_PROPERTY_NAME);
}
if (spans != null) {
for (int[] span : spans) {
if (span.length == 2 && span[0] <= offset && offset <= span[1]) {
return span;
}
}
}
return null;
} else {
int[] span = new int[2];
List<ConfigurationMetadataSource> sources = new ArrayList<>();
ConfigurationMetadataProperty property = MicronautConfigUtilities.resolveProperty(doc, offset, span, sources);
return property != null || !sources.isEmpty() ? span : null;
}
}

@Override
Expand Down Expand Up @@ -133,6 +164,7 @@ private static ElementHandle getElementHandle(ClasspathInfo cpInfo, String typeN
if (cancel != null && cancel.get()) {
return;
}
controller.toPhase(JavaSource.Phase.ELEMENTS_RESOLVED);
TypeElement te = (TypeElement) handle[0].resolve(controller);
if (te != null) {
ElementHandle found = null;
Expand Down Expand Up @@ -169,14 +201,70 @@ private static ElementHandle getElementHandle(ClasspathInfo cpInfo, String typeN
return handle[0];
}

public static final class Task extends IndexingAwareParserResultTask<Parser.Result> {

private final AtomicBoolean cancel = new AtomicBoolean();
private final Project project;

public Task(Project project) {
super(TaskIndexingMode.ALLOWED_DURING_SCAN);
this.project = project;
}

@Override
public void run(Parser.Result result, SchedulerEvent event) {
if (cancel.get()) {
return;
}
Document doc = result.getSnapshot().getSource().getDocument(false);
if (doc != null) {
List<int[]> spans = MicronautConfigUtilities.getPropertySpans(project, result);
synchronized (doc) {
doc.putProperty(SPANS_PROPERTY_NAME, spans);
}
}
}

@Override
public int getPriority() {
return 200;
}

@Override
public Class<? extends Scheduler> getSchedulerClass() {
return Scheduler.EDITOR_SENSITIVE_TASK_SCHEDULER;
}

@Override
public void cancel() {
cancel.set(true);
}

@MimeRegistration(mimeType = MicronautConfigUtilities.YAML_MIME, service = TaskFactory.class)
public static final class Factory extends TaskFactory {

@Override
public Collection<? extends SchedulerTask> create(Snapshot snapshot) {
FileObject fo = snapshot.getSource().getFileObject();
if (MicronautConfigUtilities.isMicronautConfigFile(fo)) {
Project project = FileOwnerQuery.getOwner(fo);
if (project != null && MicronautConfigProperties.hasConfigMetadata(project)) {
return Collections.singleton(new Task(project));
}
}
return Collections.emptySet();
}
}
}

public static class LocationProvider implements HyperlinkLocationProvider {

//@MimeRegistration(mimeType = "text/x-yaml", service = HyperlinkLocationProvider.class)
@MimeRegistration(mimeType = MicronautConfigUtilities.YAML_MIME, service = HyperlinkLocationProvider.class)
public static LocationProvider createYamlProvider() {
return new LocationProvider();
}

//@MimeRegistration(mimeType = "text/x-properties", service = HyperlinkLocationProvider.class)
@MimeRegistration(mimeType = MicronautConfigUtilities.PROPERTIES_MIME, service = HyperlinkLocationProvider.class)
public static LocationProvider createPropertiesProvider() {
return new LocationProvider();
}
Expand Down

0 comments on commit 836fea4

Please sign in to comment.