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 2022/04/04 12:28:15 UTC

[netbeans] branch master updated: Support for Micronaut configuration enhanced. (#3897)

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 a4911de245 Support for Micronaut configuration enhanced. (#3897)
a4911de245 is described below

commit a4911de245fbd6574244a41eca9e2ee64bf1aa87
Author: Dusan Balek <du...@oracle.com>
AuthorDate: Mon Apr 4 14:28:09 2022 +0200

    Support for Micronaut configuration enhanced. (#3897)
    
    * Navigation to custom properties declared via @Property annotation added.
    * Code completion for Micronaut config values.
    * YamlStructureItem should return non null ElementHandle as required by API.
    * Validation for Micronaut config values added.
---
 .../micronaut/MicronautConfigProperties.java       | 123 ++++++++++++++
 .../micronaut/MicronautConfigValidator.java        | 181 +++++++++++++++++++++
 .../MicronautConfigCompletionCollector.java        |   8 +
 .../MicronautConfigCompletionProvider.java         |  37 ++++-
 .../completion/MicronautConfigCompletionTask.java  | 148 ++++++++++++++---
 .../MicronautConfigHyperlinkProvider.java          |  25 ++-
 .../refactor/MicronautRefactoringFactory.java      |  34 +++-
 .../netbeans/modules/micronaut/resources/layer.xml |  14 ++
 .../modules/csl/hints/GsfErrorProvider.java        |   6 +-
 .../modules/languages/yaml/YamlParser.java         |   2 +-
 .../modules/languages/yaml/YamlSection.java        |  36 ++--
 .../modules/languages/yaml/YamlStructureItem.java  |  81 +++++----
 12 files changed, 611 insertions(+), 84 deletions(-)

diff --git a/enterprise/micronaut/src/org/netbeans/modules/micronaut/MicronautConfigProperties.java b/enterprise/micronaut/src/org/netbeans/modules/micronaut/MicronautConfigProperties.java
index 163171cae4..6a20f576e8 100644
--- a/enterprise/micronaut/src/org/netbeans/modules/micronaut/MicronautConfigProperties.java
+++ b/enterprise/micronaut/src/org/netbeans/modules/micronaut/MicronautConfigProperties.java
@@ -18,13 +18,46 @@
  */
 package org.netbeans.modules.micronaut;
 
+import com.sun.source.tree.AnnotationTree;
+import com.sun.source.tree.AssignmentTree;
+import com.sun.source.tree.ExpressionTree;
+import com.sun.source.tree.IdentifierTree;
+import com.sun.source.tree.LiteralTree;
+import com.sun.source.tree.Tree;
+import com.sun.source.util.TreePath;
+import com.sun.source.util.TreePathScanner;
+import com.sun.source.util.Trees;
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.EnumSet;
 import java.util.LinkedHashMap;
 import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.stream.Collectors;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.TypeElement;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
 import org.netbeans.api.java.classpath.ClassPath;
 import org.netbeans.api.java.project.JavaProjectConstants;
+import org.netbeans.api.java.source.ClassIndex;
+import org.netbeans.api.java.source.ClasspathInfo;
+import org.netbeans.api.java.source.CompilationController;
+import org.netbeans.api.java.source.ElementHandle;
+import org.netbeans.api.java.source.JavaSource;
+import org.netbeans.api.java.source.TypeUtilities;
 import org.netbeans.api.project.Project;
 import org.netbeans.api.project.ProjectUtils;
 import org.netbeans.api.project.SourceGroup;
+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.openide.filesystems.FileObject;
 import org.openide.util.Exceptions;
 import org.springframework.boot.configurationmetadata.ConfigurationMetadataGroup;
@@ -39,6 +72,7 @@ import org.springframework.boot.configurationmetadata.ConfigurationMetadataRepos
 public final class MicronautConfigProperties {
 
     private static final String CONFIG_METADATA_JSON = "META-INF/spring-configuration-metadata.json";
+    private static final ElementHandle<TypeElement> PROPERTY_HANDLE = ElementHandle.createTypeElementHandle(ElementKind.ANNOTATION_TYPE, "io.micronaut.context.annotation.Property");
 
     private MicronautConfigProperties() {
     }
@@ -60,6 +94,16 @@ public final class MicronautConfigProperties {
                     Exceptions.printStackTrace(ex);
                 }
             }
+            String customRepository = getCustomRepository(project);
+            if (customRepository != null) {
+                try {
+                    InputStream stream = new ByteArrayInputStream(customRepository.getBytes(StandardCharsets.UTF_8));
+                    ConfigurationMetadataRepository repository = ConfigurationMetadataRepositoryJsonBuilder.create().withJsonResource(stream).build();
+                    props.putAll(repository.getAllProperties());
+                } catch (Exception ex) {
+                    Exceptions.printStackTrace(ex);
+                }
+            }
         }
         return props;
     }
@@ -76,10 +120,89 @@ public final class MicronautConfigProperties {
                     Exceptions.printStackTrace(ex);
                 }
             }
+            String customRepository = getCustomRepository(project);
+            if (customRepository != null) {
+                try {
+                    InputStream stream = new ByteArrayInputStream(customRepository.getBytes(StandardCharsets.UTF_8));
+                    ConfigurationMetadataRepository repository = ConfigurationMetadataRepositoryJsonBuilder.create().withJsonResource(stream).build();
+                    groups.putAll(repository.getAllGroups());
+                } catch (Exception ex) {
+                    Exceptions.printStackTrace(ex);
+                }
+            }
         }
         return groups;
     }
 
+    private static String getCustomRepository(Project project) {
+        AtomicReference<String> ret = new AtomicReference<>();
+        SourceGroup[] srcGroups = ProjectUtils.getSources(project).getSourceGroups(JavaProjectConstants.SOURCES_TYPE_JAVA);
+        for (int i = 0; i < srcGroups.length; i++) {
+            SourceGroup srcGroup = srcGroups[i];
+            ClassIndex ci = ClasspathInfo.create(srcGroup.getRootFolder()).getClassIndex();
+            Set<FileObject> resources = ci.getResources(PROPERTY_HANDLE,
+                    EnumSet.of(ClassIndex.SearchKind.TYPE_REFERENCES), EnumSet.of(ClassIndex.SearchScope.SOURCE));
+            try {
+                ParserManager.parse(resources.stream().map(resource -> Source.create(resource)).collect(Collectors.toList()), new UserTask() {
+                    @Override
+                    public void run(ResultIterator resultIterator) throws Exception {
+                        CompilationController cc = CompilationController.get(resultIterator.getParserResult());
+                        cc.toPhase(JavaSource.Phase.RESOLVED);
+                        Trees trees = cc.getTrees();
+                        TypeElement te = PROPERTY_HANDLE.resolve(cc);
+                        new TreePathScanner<Void, Void>() {
+                            @Override
+                            public Void visitAnnotation(AnnotationTree node, Void p) {
+                                if (te == trees.getElement(new TreePath(getCurrentPath(), node.getAnnotationType()))) {
+                                    for (ExpressionTree argument : node.getArguments()) {
+                                        if (argument.getKind() == Tree.Kind.ASSIGNMENT) {
+                                            ExpressionTree variable = ((AssignmentTree) argument).getVariable();
+                                            ExpressionTree expression = ((AssignmentTree) argument).getExpression();
+                                            if (expression.getKind() == Tree.Kind.STRING_LITERAL && variable.getKind() == Tree.Kind.IDENTIFIER && "name".contentEquals(((IdentifierTree) variable).getName())) {
+                                                try {
+                                                    String value = (String) ((LiteralTree) expression).getValue();
+                                                    JSONObject group = new JSONObject();
+                                                    JSONObject property = new JSONObject();
+                                                    group.put("name", value);
+                                                    property.put("name", value);
+                                                    final TreePath grandParentPath = getCurrentPath().getParentPath().getParentPath();
+                                                    if (grandParentPath.getLeaf().getKind() == Tree.Kind.VARIABLE) {
+                                                        Element el = trees.getElement(grandParentPath);
+                                                        if (el != null && el.getKind().isField()) {
+                                                            String typeName = cc.getTypeUtilities().getTypeName(el.asType(), TypeUtilities.TypeNameOptions.PRINT_FQN).toString();
+                                                            String sourceTypeName = cc.getElementUtilities().getElementName(el.getEnclosingElement(), true).toString();
+                                                            property.put("type", typeName);
+                                                            property.put("sourceType", sourceTypeName);
+                                                            group.put("type", sourceTypeName);
+                                                        }
+                                                    }
+                                                    JSONArray groups = new JSONArray();
+                                                    groups.put(group);
+                                                    JSONArray properties = new JSONArray();
+                                                    properties.put(property);
+                                                    JSONObject data = new JSONObject();
+                                                    data.put("groups", groups);
+                                                    data.put("properties", properties);
+                                                    ret.set(data.toString());
+                                                } catch (JSONException ex) {
+                                                    Exceptions.printStackTrace(ex);
+                                                }
+                                            }
+                                        }
+                                    }
+                                }
+                                return super.visitAnnotation(node, p);
+                            }
+                        }.scan(new TreePath(cc.getCompilationUnit()), null);
+                    }
+                });
+            } catch (ParseException ex) {
+                Exceptions.printStackTrace(ex);
+            }
+        }
+        return ret.get();
+    }
+
     private static ClassPath getExecuteClasspath(Project project) {
         SourceGroup[] srcGroups = ProjectUtils.getSources(project).getSourceGroups(JavaProjectConstants.SOURCES_TYPE_JAVA);
         if (srcGroups.length > 0) {
diff --git a/enterprise/micronaut/src/org/netbeans/modules/micronaut/MicronautConfigValidator.java b/enterprise/micronaut/src/org/netbeans/modules/micronaut/MicronautConfigValidator.java
new file mode 100644
index 0000000000..a30c5c2463
--- /dev/null
+++ b/enterprise/micronaut/src/org/netbeans/modules/micronaut/MicronautConfigValidator.java
@@ -0,0 +1,181 @@
+/*
+ * 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.io.IOException;
+import java.util.List;
+import java.util.Map;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.TypeElement;
+import org.netbeans.api.editor.document.EditorDocumentUtils;
+import org.netbeans.api.java.source.ClasspathInfo;
+import org.netbeans.api.java.source.JavaSource;
+import org.netbeans.api.project.FileOwnerQuery;
+import org.netbeans.api.project.Project;
+import org.netbeans.modules.csl.api.Error;
+import org.netbeans.modules.csl.api.Hint;
+import org.netbeans.modules.csl.api.HintsProvider;
+import org.netbeans.modules.csl.api.Rule;
+import org.netbeans.modules.csl.api.RuleContext;
+import org.netbeans.modules.csl.api.Severity;
+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.DefaultError;
+import org.netbeans.modules.csl.spi.ParserResult;
+import org.openide.filesystems.FileObject;
+import org.openide.util.Exceptions;
+import org.openide.util.NbBundle;
+import org.springframework.boot.configurationmetadata.ConfigurationMetadataProperty;
+
+/**
+ *
+ * @author Dusan Balek
+ */
+public class MicronautConfigValidator implements HintsProvider {
+
+    @Override
+    public void computeHints(HintsManager manager, RuleContext context, List<Hint> hints) {
+    }
+
+    @Override
+    public void computeSuggestions(HintsManager manager, RuleContext context, List<Hint> suggestions, int caretOffset) {
+    }
+
+    @Override
+    public void computeSelectionHints(HintsManager manager, RuleContext context, List<Hint> suggestions, int start, int end) {
+    }
+
+    @Override
+    public void computeErrors(HintsManager manager, RuleContext context, List<Hint> hints, List<Error> unhandled) {
+        FileObject fo = EditorDocumentUtils.getFileObject(context.doc);
+        if (MicronautConfigUtilities.isMicronautConfigFile(fo)) {
+            final ParserResult parserResult = context.parserResult;
+            final List<? extends Error> diagnostics = parserResult.getDiagnostics();
+            if (diagnostics != null && !diagnostics.isEmpty()) {
+                unhandled.addAll(diagnostics);
+            }
+            final Project project = FileOwnerQuery.getOwner(fo);
+            if (project != null) {
+                if (MicronautConfigProperties.hasConfigMetadata(project)) {
+                    final Language language = LanguageRegistry.getInstance().getLanguageByMimeType(parserResult.getSnapshot().getMimeType());
+                    if (language != null) {
+                        final StructureScanner scanner = language.getStructure();
+                        if (scanner != null) {
+                            final List<? extends StructureItem> structures = scanner.scan(parserResult);
+                            if (!structures.isEmpty()) {
+                                final Map<String, ConfigurationMetadataProperty> properties = MicronautConfigProperties.getProperties(project);
+                                if (!properties.isEmpty()) {
+                                    validate(parserResult, structures, "", properties, unhandled);
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    @Override
+    public void cancel() {
+    }
+
+    @Override
+    public List<Rule> getBuiltinRules() {
+        return null;
+    }
+
+    @Override
+    public RuleContext createRuleContext() {
+        return new RuleContext();
+    }
+
+    @NbBundle.Messages({
+        "MSG_InvalidValue=Invalid value \"{0}\", must be of type \"{1}\"",
+    })
+    private static void validate(ParserResult parserResult, List<? extends StructureItem> structure, String name, Map<String, ConfigurationMetadataProperty> properties, List<Error> errors) {
+        final FileObject fileObject = parserResult.getSnapshot().getSource().getFileObject();
+        final CharSequence text = parserResult.getSnapshot().getText();
+        for (StructureItem item : structure) {
+            final String fullName = name.isEmpty() ? item.getName() : name + '.' + item.getName();
+            List<? extends StructureItem> nestedItems = item.getNestedItems();
+            if (nestedItems.isEmpty()) {
+                final ConfigurationMetadataProperty property = properties.get(fullName);
+                if (property != null) {
+                    final String type = property.getType();
+                    if (type != null && !"java.lang.String".equals(type)) {
+                        try {
+                            final String itemText = text.subSequence((int) item.getPosition(), (int) item.getEndPosition()).toString();
+                            int idx = itemText.lastIndexOf(':');
+                            if (idx >= 0) {
+                                final String value = itemText.substring(idx + 1).trim();
+                                if (!value.isEmpty()) {
+                                    int start = (int) item.getPosition() + itemText.indexOf(value, idx + 1);
+                                    int end = start + value.length();
+                                    switch (type) {
+                                        case "boolean":
+                                            if (!"true".equalsIgnoreCase(value) && !"false".equalsIgnoreCase(value)) {
+                                                errors.add(DefaultError.createDefaultError(null, Bundle.MSG_InvalidValue(value, type), null, fileObject, start, end, false, Severity.ERROR));
+                                            }
+                                            break;
+                                        case "java.lang.Integer":
+                                            try {
+                                                Integer.parseInt(value);
+                                            } catch (NumberFormatException e) {
+                                                errors.add(DefaultError.createDefaultError(null, Bundle.MSG_InvalidValue(value, type), null, fileObject, start, end, false, Severity.ERROR));
+                                            }
+                                            break;
+                                        case "java.lang.Long":
+                                            try {
+                                                Long.parseLong(value);
+                                            } catch (NumberFormatException e) {
+                                                errors.add(DefaultError.createDefaultError(null, Bundle.MSG_InvalidValue(value, type), null, fileObject, start, end, false, Severity.ERROR));
+                                            }
+                                            break;
+                                        default:
+                                            JavaSource js = JavaSource.create(ClasspathInfo.create(fileObject));
+                                            if (js != null) {
+                                                try {
+                                                    js.runUserActionTask(cc -> {
+                                                        cc.toPhase(JavaSource.Phase.ELEMENTS_RESOLVED);
+                                                        TypeElement typeElement = cc.getElements().getTypeElement(type);
+                                                        if (typeElement != null && typeElement.getKind() == ElementKind.ENUM) {
+                                                            if (!typeElement.getEnclosedElements().stream().anyMatch(e -> e.getKind() == ElementKind.ENUM_CONSTANT && value.contentEquals(e.getSimpleName()))) {
+                                                                errors.add(DefaultError.createDefaultError(null, Bundle.MSG_InvalidValue(value, type), null, fileObject, start, end, false, Severity.ERROR));
+                                                            }
+                                                        }
+                                                    }, true);
+                                                } catch (IOException ioe) {
+                                                    Exceptions.printStackTrace(ioe);
+                                                }
+                                            }
+                                    }
+                                }
+                            }
+                        } catch (IndexOutOfBoundsException e) {
+                        }
+                    }
+                }
+            } else {
+                validate(parserResult, nestedItems, fullName, properties, errors);
+            }
+        }
+    }
+}
diff --git a/enterprise/micronaut/src/org/netbeans/modules/micronaut/completion/MicronautConfigCompletionCollector.java b/enterprise/micronaut/src/org/netbeans/modules/micronaut/completion/MicronautConfigCompletionCollector.java
index 06d40ba545..893f858898 100644
--- a/enterprise/micronaut/src/org/netbeans/modules/micronaut/completion/MicronautConfigCompletionCollector.java
+++ b/enterprise/micronaut/src/org/netbeans/modules/micronaut/completion/MicronautConfigCompletionCollector.java
@@ -119,6 +119,14 @@ public class MicronautConfigCompletionCollector implements CompletionCollector {
                             }
                             return builder.build();
                         }
+
+                        @Override
+                        public Completion createValueItem(String value, int offset, boolean isEnum) {
+                            return CompletionCollector.newBuilder(value)
+                                    .kind(isEnum ? Completion.Kind.EnumMember : Completion.Kind.Keyword)
+                                    .sortText(String.format("%04d%s", 5, value))
+                                    .build();
+                        }
                     }).stream().forEach(consumer);
                 }
             }
diff --git a/enterprise/micronaut/src/org/netbeans/modules/micronaut/completion/MicronautConfigCompletionProvider.java b/enterprise/micronaut/src/org/netbeans/modules/micronaut/completion/MicronautConfigCompletionProvider.java
index 92456a7b89..f5417bb0ea 100644
--- a/enterprise/micronaut/src/org/netbeans/modules/micronaut/completion/MicronautConfigCompletionProvider.java
+++ b/enterprise/micronaut/src/org/netbeans/modules/micronaut/completion/MicronautConfigCompletionProvider.java
@@ -110,7 +110,10 @@ public class MicronautConfigCompletionProvider implements CompletionProvider {
 
     private static class MicronautConfigCompletionQuery extends AsyncCompletionQuery {
 
-        private static final String ICON = "org/netbeans/modules/editor/resources/completion/field_16.png"; //NOI18N
+        private static final String FIELD_ICON = "org/netbeans/modules/editor/resources/completion/field_16.png"; //NOI18N
+        private static final String KEYWORD_ICON = "org/netbeans/modules/java/editor/resources/javakw_16.png"; //NOI18N
+        private static final String FIELD_COLOR = getHTMLColor(64, 198, 88);
+        private static final String KEYWORD_COLOR = getHTMLColor(64, 64, 217);
         private static final Pattern FQN = Pattern.compile("(\\w+\\.)+(\\w+)");
 
         private final Project project;
@@ -128,7 +131,7 @@ public class MicronautConfigCompletionProvider implements CompletionProvider {
                     String propName = property.getId();
                     String propType = property.getType();
                     CompletionUtilities.CompletionItemBuilder builder = CompletionUtilities.newCompletionItemBuilder(propName)
-                            .iconResource(ICON)
+                            .iconResource(FIELD_ICON)
                             .leftHtmlText(property.isDeprecated()
                                     ? PROPERTY_NAME_COLOR + "<s>" + propName + "</s></font>"
                                     : PROPERTY_NAME_COLOR + propName + "</font>")
@@ -211,7 +214,7 @@ public class MicronautConfigCompletionProvider implements CompletionProvider {
                 public CompletionItem createTopLevelPropertyItem(String propName, int offset, int baseIndent, int indentLevelSize) {
                     resultSet.setAnchorOffset(offset);
                     return CompletionUtilities.newCompletionItemBuilder(propName)
-                            .iconResource(ICON)
+                            .iconResource(FIELD_ICON)
                             .leftHtmlText(PROPERTY_NAME_COLOR + "<b>" + propName + "</b></font>")
                             .sortPriority(10)
                             .onSelect(ctx -> {
@@ -249,6 +252,34 @@ public class MicronautConfigCompletionProvider implements CompletionProvider {
                             })
                             .build();
                 }
+
+                @Override
+                public CompletionItem createValueItem(String value, int offset, boolean isEnum) {
+                    resultSet.setAnchorOffset(offset);
+                    return CompletionUtilities.newCompletionItemBuilder(value)
+                            .iconResource(isEnum ? FIELD_ICON : KEYWORD_ICON)
+                            .leftHtmlText(isEnum ? KEYWORD_COLOR + "<b>" + value + "</b></font>" : FIELD_COLOR + value + "</font>")
+                            .sortPriority(5)
+                            .onSelect(ctx -> {
+                                try {
+                                    Document doc = ctx.getComponent().getDocument();
+                                    LineDocument lineDocument = LineDocumentUtils.as(doc, LineDocument.class);
+                                    if (lineDocument != null) {
+                                        int caretOffset = ctx.getComponent().getCaretPosition();
+                                        int end = LineDocumentUtils.getWordEnd(lineDocument, caretOffset);
+                                        if (ctx.isOverwrite() && LineDocumentUtils.getWordStart(lineDocument, end) == offset) {
+                                            doc.remove(offset, Math.max(caretOffset, end) - offset);
+                                        } else if (offset < caretOffset) {
+                                            doc.remove(offset, caretOffset - offset);
+                                        }
+                                        doc.insertString(offset, value, null);
+                                    }
+                                } catch (BadLocationException ex) {
+                                    Exceptions.printStackTrace(ex);
+                                }
+                            })
+                            .build();
+                }
             }));
             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
index f94582fd59..bcd5031d42 100644
--- a/enterprise/micronaut/src/org/netbeans/modules/micronaut/completion/MicronautConfigCompletionTask.java
+++ b/enterprise/micronaut/src/org/netbeans/modules/micronaut/completion/MicronautConfigCompletionTask.java
@@ -18,6 +18,7 @@
  */
 package org.netbeans.modules.micronaut.completion;
 
+import java.io.IOException;
 import java.io.StringReader;
 import java.util.ArrayList;
 import java.util.Collections;
@@ -26,11 +27,21 @@ import java.util.List;
 import java.util.Map;
 import java.util.Properties;
 import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import static javax.lang.model.element.ElementKind.ENUM_CONSTANT;
+import javax.lang.model.element.TypeElement;
 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.java.project.JavaProjectConstants;
+import org.netbeans.api.java.source.ClasspathInfo;
+import org.netbeans.api.java.source.JavaSource;
 import org.netbeans.api.project.Project;
+import org.netbeans.api.project.ProjectUtils;
+import org.netbeans.api.project.SourceGroup;
 import org.netbeans.lib.editor.util.swing.DocumentUtilities;
 import org.netbeans.modules.csl.api.StructureItem;
 import org.netbeans.modules.csl.api.StructureScanner;
@@ -56,6 +67,7 @@ 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);
+        T createValueItem(String value, int offset, boolean isEnum);
     }
 
     public <T> List<T> query(Document doc, int caretOffset, Project project, ItemFactory<T> factory) {
@@ -95,7 +107,6 @@ public class MicronautConfigCompletionTask {
                                                         currentItem = item;
                                                     }
                                                 }
-                                                int filterLength = filter.length();
                                                 if (prefix != null) {
                                                     filter += prefix;
                                                 }
@@ -124,37 +135,77 @@ public class MicronautConfigCompletionTask {
                                     }
                                 }
                             });
+                        } else {
+                            final int prevWS = LineDocumentUtils.getPreviousWhitespace(lineDocument, caretOffset);
+                            final int wordStart = (prevWS < lineStart + idx ? lineStart + idx : prevWS) + 1;
+                            final String prefix = wordStart < caretOffset ? text.substring(wordStart - lineStart) : "";
+                            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);
+                                                List<? extends StructureItem> context = getContext(structures, wordStart);
+                                                String propName = "";
+                                                for (StructureItem structureItem : context) {
+                                                    propName += propName.isEmpty() ? structureItem.getName() : '.' + structureItem.getName();
+                                                }
+                                                if (!propName.isEmpty()) {
+                                                    items.addAll(completeValues(propName, prefix, anchorOffset, project, factory));
+                                                }
+                                            }
+                                        }
+                                    }
+                                }
+                            });
                         }
                     }
                 } else {
                     if (!text.startsWith("#") && !text.startsWith("!")) {
                         int colIdx = text.indexOf(':');
                         int eqIdx = text.indexOf('=');
-                        if (colIdx < 0 && eqIdx < 0) {
-                            final int wordStart = LineDocumentUtils.getPreviousWhitespace(lineDocument, caretOffset) + 1;
-                            final String prefix = wordStart < caretOffset ? text.substring(wordStart - lineStart) : "";
-                            final int lastDotIdx = prefix.lastIndexOf('.');
-                            final int anchorOffset = wordStart < caretOffset ? wordStart + (lastDotIdx < 0 ? 0 : lastDotIdx + 1) : caretOffset;
-                            final Properties props = new Properties();
-                            props.load(new StringReader(doc.getText(0, doc.getLength())));
-                            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, prefix);
-                                if (idx >= 0) {
-                                    int dotIdx = propName.indexOf('.', idx);
-                                    String simpleName = dotIdx < 0 ? propName.substring(idx) : propName.substring(idx, dotIdx);
-                                    if (props.getProperty(propName) == null) {
-                                        items.add(factory.createPropertyItem(entry.getValue(), anchorOffset, -1, -1, idx));
-                                        if (dotIdx > 0) {
-                                            topLevels.add(simpleName);
+                        if (colIdx < 0) {
+                            if (eqIdx < 0) {
+                                final int wordStart = LineDocumentUtils.getPreviousWhitespace(lineDocument, caretOffset) + 1;
+                                final String prefix = wordStart < caretOffset ? text.substring(wordStart - lineStart) : "";
+                                final int lastDotIdx = prefix.lastIndexOf('.');
+                                final int anchorOffset = wordStart < caretOffset ? wordStart + (lastDotIdx < 0 ? 0 : lastDotIdx + 1) : caretOffset;
+                                final Properties props = new Properties();
+                                props.load(new StringReader(doc.getText(0, doc.getLength())));
+                                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, prefix);
+                                    if (idx >= 0) {
+                                        int dotIdx = propName.indexOf('.', idx);
+                                        String simpleName = dotIdx < 0 ? propName.substring(idx) : propName.substring(idx, dotIdx);
+                                        if (props.getProperty(propName) == null) {
+                                            items.add(factory.createPropertyItem(entry.getValue(), anchorOffset, -1, -1, idx));
+                                            if (dotIdx > 0) {
+                                                topLevels.add(simpleName);
+                                            }
                                         }
                                     }
                                 }
-                            }
-                            for (String topLevel : topLevels) {
-                                items.add(factory.createTopLevelPropertyItem(topLevel, anchorOffset, -1, -1));
+                                for (String topLevel : topLevels) {
+                                    items.add(factory.createTopLevelPropertyItem(topLevel, anchorOffset, -1, -1));
+                                }
+                            } else {
+                                int wordStart = LineDocumentUtils.getPreviousWhitespace(lineDocument, caretOffset) + 1;
+                                if (wordStart < lineStart + eqIdx) {
+                                    wordStart = lineStart + eqIdx + 1;
+                                }
+                                final String prefix = wordStart < caretOffset ? text.substring(wordStart - lineStart) : "";
+                                final int anchorOffset = wordStart < caretOffset ? wordStart : caretOffset;
+                                final String propName = text.substring(0, eqIdx).trim();
+                                if (!propName.isEmpty()) {
+                                    items.addAll(completeValues(propName, prefix, anchorOffset, project, factory));
+                                }
                             }
                         }
                     }
@@ -166,6 +217,57 @@ public class MicronautConfigCompletionTask {
         return items;
     }
 
+    private <T> List<T> completeValues(String propName, String prefix, int anchorOffset, Project project, ItemFactory<T> factory) {
+        List<T> items = new ArrayList<>();
+        Map<String, ConfigurationMetadataProperty> properties = MicronautConfigProperties.getProperties(project);
+        ConfigurationMetadataProperty property = properties.get(propName);
+        if (property != null) {
+            String type = property.getType();
+            if (type != null) {
+                if ("boolean".equals(type)) {
+                    if ("true".startsWith(prefix)) {
+                        items.add(factory.createValueItem("true", anchorOffset, false));
+                    }
+                    if ("false".startsWith(prefix)) {
+                        items.add(factory.createValueItem("false", anchorOffset, false));
+                    }
+                } else {
+                    SourceGroup[] srcGroups = ProjectUtils.getSources(project).getSourceGroups(JavaProjectConstants.SOURCES_TYPE_JAVA);
+                    AtomicBoolean resolved = new AtomicBoolean();
+                    for (SourceGroup srcGroup : srcGroups) {
+                        if (!resolved.get()) {
+                            JavaSource js = JavaSource.create(ClasspathInfo.create(srcGroup.getRootFolder()));
+                            if (js != null) {
+                                try {
+                                    js.runUserActionTask(cc -> {
+                                        cc.toPhase(JavaSource.Phase.ELEMENTS_RESOLVED);
+                                        TypeElement typeElement = cc.getElements().getTypeElement(type);
+                                        if (typeElement != null) {
+                                            resolved.set(true);
+                                            if (typeElement.getKind() == ElementKind.ENUM) {
+                                                for (Element e : typeElement.getEnclosedElements()) {
+                                                    if (e.getKind() == ENUM_CONSTANT) {
+                                                        String name = e.getSimpleName().toString();
+                                                        if (name.startsWith(prefix)) {
+                                                            items.add(factory.createValueItem(name, anchorOffset, true));
+                                                        }
+                                                    }
+                                                }
+                                            }
+                                        }
+                                    }, true);
+                                } catch (IOException ex) {
+                                    Exceptions.printStackTrace(ex);
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        return items;
+    }
+
     private int match(String propName, String filter) {
         int len = 0;
         String[] parts = propName.split("\\*");
diff --git a/enterprise/micronaut/src/org/netbeans/modules/micronaut/hyperlink/MicronautConfigHyperlinkProvider.java b/enterprise/micronaut/src/org/netbeans/modules/micronaut/hyperlink/MicronautConfigHyperlinkProvider.java
index 608beba914..e6f243d0d3 100644
--- a/enterprise/micronaut/src/org/netbeans/modules/micronaut/hyperlink/MicronautConfigHyperlinkProvider.java
+++ b/enterprise/micronaut/src/org/netbeans/modules/micronaut/hyperlink/MicronautConfigHyperlinkProvider.java
@@ -23,12 +23,16 @@ import java.io.IOException;
 import java.util.ArrayList;
 import java.util.EnumSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.atomic.AtomicBoolean;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.AnnotationValue;
 import javax.lang.model.element.ElementKind;
 import javax.lang.model.element.ExecutableElement;
 import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
 import javax.lang.model.util.ElementFilter;
 import javax.swing.text.Document;
 import org.netbeans.api.editor.mimelookup.MimeRegistration;
@@ -129,13 +133,32 @@ public class MicronautConfigHyperlinkProvider implements HyperlinkProviderExt {
                         }
                         TypeElement te = (TypeElement) handle[0].resolve(controller);
                         if (te != null) {
+                            ElementHandle found = null;
                             String name = "set" + propertyName.replace("-", "");
                             for (ExecutableElement executableElement : ElementFilter.methodsIn(te.getEnclosedElements())) {
                                 if (name.equalsIgnoreCase(executableElement.getSimpleName().toString())) {
-                                    handle[0] = ElementHandle.create(executableElement);
+                                    found = ElementHandle.create(executableElement);
                                     break;
                                 }
                             }
+                            if (found == null) {
+                                TypeElement typeElement = controller.getElements().getTypeElement("io.micronaut.context.annotation.Property");
+                                for (VariableElement variableElement : ElementFilter.fieldsIn(te.getEnclosedElements())) {
+                                    for (AnnotationMirror annotationMirror : variableElement.getAnnotationMirrors()) {
+                                        if (typeElement == annotationMirror.getAnnotationType().asElement()) {
+                                            for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> entry : annotationMirror.getElementValues().entrySet()) {
+                                                if ("name".contentEquals(entry.getKey().getSimpleName()) && propertyName.equals(entry.getValue().getValue())) {
+                                                    found = ElementHandle.create(variableElement);
+                                                    break;
+                                                }
+                                            }
+                                        }
+                                    }
+                                }
+                            }
+                            if (found != null) {
+                                handle[0] = found;
+                            }
                         }
                     }, true);
                 } catch (IOException ex) {}
diff --git a/enterprise/micronaut/src/org/netbeans/modules/micronaut/refactor/MicronautRefactoringFactory.java b/enterprise/micronaut/src/org/netbeans/modules/micronaut/refactor/MicronautRefactoringFactory.java
index 00b4bdf22c..9bb887a352 100644
--- a/enterprise/micronaut/src/org/netbeans/modules/micronaut/refactor/MicronautRefactoringFactory.java
+++ b/enterprise/micronaut/src/org/netbeans/modules/micronaut/refactor/MicronautRefactoringFactory.java
@@ -22,7 +22,10 @@ import java.io.IOException;
 import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.AnnotationValue;
 import javax.lang.model.element.Element;
 import javax.lang.model.element.ElementKind;
 import javax.lang.model.element.ExecutableElement;
@@ -134,13 +137,13 @@ public class MicronautRefactoringFactory implements RefactoringPluginFactory {
                                     if (info == null) {
                                         info = getInfo();
                                     }
-                                    if (info.className != null && info.methodName != null && info.methodName.startsWith("set")) {
+                                    if (info.className != null && info.propertyName != null) {
                                         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().replace("-", "");
-                                                    if (name.equalsIgnoreCase(info.methodName)) {
+                                                    String name = property.getName().replace("-", "");
+                                                    if (name.equalsIgnoreCase(info.propertyName)) {
                                                         for (FileObject configFile : configFiles) {
                                                             MicronautConfigUtilities.collectUsages(configFile, property.getId(), usage -> {
                                                                 refactoringElements.add(refactoring, new WhereUsedRefactoringElement(usage.getFileObject(), usage.getStartOffset(), usage.getEndOffset(), usage.getText()));
@@ -169,9 +172,26 @@ public class MicronautRefactoringFactory implements RefactoringPluginFactory {
                 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();
+                    if (el != null) {
+                        if (el.getKind() == ElementKind.METHOD) {
+                            if (el.getModifiers().contains(Modifier.PUBLIC) && el.getSimpleName().toString().startsWith("set")) {
+                                info.propertyName = el.getSimpleName().toString().substring(3);
+                                info.className = ((TypeElement) el.getEnclosingElement()).getQualifiedName().toString();
+                            }
+                        } else if (el.getKind().isField()) {
+                            TypeElement typeElement = controller.getElements().getTypeElement("io.micronaut.context.annotation.Property");
+                            for (AnnotationMirror annotationMirror : el.getAnnotationMirrors()) {
+                                if (typeElement == annotationMirror.getAnnotationType().asElement()) {
+                                    for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> entry : annotationMirror.getElementValues().entrySet()) {
+                                        if ("name".contentEquals(entry.getKey().getSimpleName())) {
+                                            info.propertyName = (String) entry.getValue().getValue();
+                                            info.className = ((TypeElement) el.getEnclosingElement()).getQualifiedName().toString();
+                                            break;
+                                        }
+                                    }
+                                }
+                            }
+                        }
                     }
                 }, true);
             }
@@ -180,7 +200,7 @@ public class MicronautRefactoringFactory implements RefactoringPluginFactory {
 
         private static class Info {
             private String className;
-            private String methodName;
+            private String propertyName;
         }
     }
 
diff --git a/enterprise/micronaut/src/org/netbeans/modules/micronaut/resources/layer.xml b/enterprise/micronaut/src/org/netbeans/modules/micronaut/resources/layer.xml
index b103120a3d..2a73a7cbf7 100644
--- a/enterprise/micronaut/src/org/netbeans/modules/micronaut/resources/layer.xml
+++ b/enterprise/micronaut/src/org/netbeans/modules/micronaut/resources/layer.xml
@@ -28,6 +28,20 @@
                     <file name="org-netbeans-modules-micronaut-completion-MicronautJavaConfigPropertiesCompletion.instance"/>
                 </folder>
             </folder>
+            <folder name="x-yaml">
+                <file name="org-netbeans-modules-csl-hints-GsfErrorProvider.instance">
+                    <attr name="instanceClass" stringvalue="org.netbeans.modules.csl.hints.GsfErrorProvider"/>
+                </file>
+            </folder>
+        </folder>
+    </folder>
+    <folder name="CslPlugins">
+        <folder name="text">
+            <folder name="x-yaml">
+                <file name="hints.instance">
+                    <attr name="instanceClass" stringvalue="org.netbeans.modules.micronaut.MicronautConfigValidator"/>
+                </file>
+            </folder>
         </folder>
     </folder>
     <folder name="Projects">
diff --git a/ide/csl.api/src/org/netbeans/modules/csl/hints/GsfErrorProvider.java b/ide/csl.api/src/org/netbeans/modules/csl/hints/GsfErrorProvider.java
index ca0eab0f50..4754ed03ed 100644
--- a/ide/csl.api/src/org/netbeans/modules/csl/hints/GsfErrorProvider.java
+++ b/ide/csl.api/src/org/netbeans/modules/csl/hints/GsfErrorProvider.java
@@ -112,20 +112,20 @@ public class GsfErrorProvider implements ErrorProvider {
 
     private Diagnostic error2Diagnostic(Error error, LineDocument lineDocument, int idx) {
         Diagnostic.Builder diagBuilder = Diagnostic.Builder.create(() -> {
-            if (lineDocument != null) {
+            if (lineDocument != null && error.getStartPosition() >= error.getEndPosition()) {
                 try {
                     return LineDocumentUtils.getLineFirstNonWhitespace(lineDocument, error.getStartPosition());
                 } catch (BadLocationException ex) {}
             }
             return error.getStartPosition();
         }, () -> {
-            if (lineDocument != null) {
+            if (lineDocument != null && error.getStartPosition() >= error.getEndPosition()) {
                 try {
                     return LineDocumentUtils.getLineLastNonWhitespace(lineDocument, error.getEndPosition());
                 } catch (BadLocationException ex) {}
             }
             return error.getEndPosition();
-        }, error.getDescription());
+        }, error.getDescription() != null ? error.getDescription() : error.getDisplayName());
         switch (error.getSeverity()) {
             case FATAL:
             case ERROR:
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 55d863d8fd..653f62837d 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
@@ -195,7 +195,7 @@ public class YamlParser extends org.netbeans.modules.parsing.spi.Parser {
             if (stallCounter < 2) {
                 YamlSection section = sources.pop();
                 try {
-                    List<?  extends StructureItem> items = section.collectItems();
+                    List<?  extends StructureItem> items = section.collectItems(snapshot);
                     result.addStructure(items);
                 } catch (ScannerException se) {
                     result.addError(section.processException(snapshot, se));
diff --git a/ide/languages.yaml/src/org/netbeans/modules/languages/yaml/YamlSection.java b/ide/languages.yaml/src/org/netbeans/modules/languages/yaml/YamlSection.java
index 131e7af2e4..f0b4b17539 100644
--- a/ide/languages.yaml/src/org/netbeans/modules/languages/yaml/YamlSection.java
+++ b/ide/languages.yaml/src/org/netbeans/modules/languages/yaml/YamlSection.java
@@ -104,7 +104,7 @@ public class YamlSection {
         return source.length();
     }
 
-    List<? extends StructureItem> collectItems() {
+    List<? extends StructureItem> collectItems(Snapshot snapshot) {
         if (parser != null) {
             throw new IllegalStateException("This YAML segment is already parsed.");
         }
@@ -112,7 +112,7 @@ public class YamlSection {
         ScannerImpl scanner = new ScannerImpl(SETTINGS, new StreamReader(SETTINGS, source));
         parser = new ParserImpl(SETTINGS, scanner);
         while (parser.hasNext()) {
-            YamlStructureItem root = processItem();
+            YamlStructureItem root = processItem(snapshot);
             if (root != null) {
                 ret.addAll(root.getNestedItems());
             }
@@ -120,21 +120,21 @@ public class YamlSection {
         return ret;
     }
 
-    private YamlStructureItem processItem() {
+    private YamlStructureItem processItem(Snapshot snapshot) {
         YamlStructureItem ret = null;
         while ((ret == null) && parser.hasNext()) {
             switch (parser.peekEvent().getEventId()) {
                 case MappingStart:
-                    ret = processMapping((MappingStartEvent) parser.next());
+                    ret = processMapping(snapshot, (MappingStartEvent) parser.next());
                     break;
                 case SequenceStart:
-                    ret = processSequence((SequenceStartEvent) parser.next());
+                    ret = processSequence(snapshot, (SequenceStartEvent) parser.next());
                     break;
                 case Scalar:
-                    ret = processScalar((ScalarEvent) parser.next());
+                    ret = processScalar(snapshot, (ScalarEvent) parser.next());
                     break;
                 case Alias:
-                    ret = processAlias((AliasEvent) parser.next());
+                    ret = processAlias(snapshot, (AliasEvent) parser.next());
                     break;
                 default:
                     parser.next();
@@ -143,19 +143,19 @@ public class YamlSection {
         return ret;
     }
 
-    private YamlStructureItem processAlias(AliasEvent evt) {
-        return new YamlStructureItem.Simple(ALIAS, evt.getAlias().getValue(), getIndex(evt.getStartMark()), getIndex(evt.getEndMark()));
+    private YamlStructureItem processAlias(Snapshot snapshot, AliasEvent evt) {
+        return new YamlStructureItem.Simple(ALIAS, snapshot.getSource().getFileObject(), evt.getAlias().getValue(), getIndex(evt.getStartMark()), getIndex(evt.getEndMark()));
     }
 
-    private YamlStructureItem processScalar(ScalarEvent evt) {
-        return new YamlStructureItem.Simple(SCALAR, evt.getValue(), getIndex(evt.getStartMark()), getIndex(evt.getEndMark()));
+    private YamlStructureItem processScalar(Snapshot snapshot, ScalarEvent evt) {
+        return new YamlStructureItem.Simple(SCALAR, snapshot.getSource().getFileObject(), evt.getValue(), getIndex(evt.getStartMark()), getIndex(evt.getEndMark()));
     }
 
-    private YamlStructureItem processMapping(MappingStartEvent evt) {
-        YamlStructureItem.Collection item = new YamlStructureItem.Collection(MAP, getIndex(evt.getStartMark()));
+    private YamlStructureItem processMapping(Snapshot snapshot, MappingStartEvent evt) {
+        YamlStructureItem.Collection item = new YamlStructureItem.Collection(MAP, snapshot.getSource().getFileObject(), getIndex(evt.getStartMark()));
         while (parser.hasNext() && !parser.checkEvent(Event.ID.MappingEnd)) {
-            YamlStructureItem keyItem = processItem();
-            YamlStructureItem valueItem = processItem();
+            YamlStructureItem keyItem = processItem(snapshot);
+            YamlStructureItem valueItem = processItem(snapshot);
             item.add(new YamlStructureItem.MapEntry(keyItem, valueItem));
         }
         if (parser.hasNext()) {
@@ -167,10 +167,10 @@ public class YamlSection {
         return item;
     }
 
-    private YamlStructureItem processSequence(SequenceStartEvent evt) {
-        YamlStructureItem.Collection item = new YamlStructureItem.Collection(SEQUENCE, getIndex(evt.getStartMark()));
+    private YamlStructureItem processSequence(Snapshot snapshot, SequenceStartEvent evt) {
+        YamlStructureItem.Collection item = new YamlStructureItem.Collection(SEQUENCE, snapshot.getSource().getFileObject(), getIndex(evt.getStartMark()));
         while (parser.hasNext() && !parser.checkEvent(Event.ID.SequenceEnd)) {
-            item.add(processItem());
+            item.add(processItem(snapshot));
         }
         if (parser.hasNext()) {
             SequenceEndEvent eevt = (SequenceEndEvent) parser.next();
diff --git a/ide/languages.yaml/src/org/netbeans/modules/languages/yaml/YamlStructureItem.java b/ide/languages.yaml/src/org/netbeans/modules/languages/yaml/YamlStructureItem.java
index 8ee86132c0..2fcec4ec35 100644
--- a/ide/languages.yaml/src/org/netbeans/modules/languages/yaml/YamlStructureItem.java
+++ b/ide/languages.yaml/src/org/netbeans/modules/languages/yaml/YamlStructureItem.java
@@ -30,24 +30,31 @@ import org.netbeans.modules.csl.api.ElementHandle;
 import org.netbeans.modules.csl.api.ElementKind;
 import org.netbeans.modules.csl.api.HtmlFormatter;
 import org.netbeans.modules.csl.api.Modifier;
+import org.netbeans.modules.csl.api.OffsetRange;
 import org.netbeans.modules.csl.api.StructureItem;
+import org.netbeans.modules.csl.spi.ParserResult;
+import org.openide.filesystems.FileObject;
 import org.openide.xml.XMLUtil;
 
 /**
  *
  * @author lkishalmi
  */
-public abstract class YamlStructureItem implements StructureItem {
+public abstract class YamlStructureItem implements StructureItem, ElementHandle {
 
     public static enum NodeType { MAP, SEQUENCE, MAPPING, SCALAR, ALIAS };
     
     private static final Logger LOGGER = Logger.getLogger(YamlStructureItem.class.getName());
     private final NodeType type;
+    private final FileObject fileObject;
     private final long startMark;
+    private long endMark;
     
-    public YamlStructureItem(NodeType type, long startMark) {
+    public YamlStructureItem(NodeType type, FileObject fileObject, long startMark, long endMark) {
         this.type = type;
+        this.fileObject = fileObject;
         this.startMark = startMark;
+        this.endMark = endMark;
     }
 
     public NodeType getType() {
@@ -72,7 +79,7 @@ public abstract class YamlStructureItem implements StructureItem {
 
     @Override
     public ElementHandle getElementHandle() {
-        return null;
+        return this;
     }
 
     @Override
@@ -90,30 +97,65 @@ public abstract class YamlStructureItem implements StructureItem {
         return getNestedItems().isEmpty();
     }
 
+    @Override
+    public FileObject getFileObject() {
+        return fileObject;
+    }
+
     @Override
     public long getPosition() {
         return startMark;
     }
 
+    @Override
+    public long getEndPosition() {
+        return endMark;
+    }
+
+    @Override
+    public OffsetRange getOffsetRange(ParserResult result) {
+        return new OffsetRange((int) startMark, (int) endMark);
+    }
+
     @Override
     public ImageIcon getCustomIcon() {
         return null;
     }
 
+    @Override
+    public String getIn() {
+        return null;
+    }
+
+    @Override
+    public String getMimeType() {
+        return YamlTokenId.YAML_MIME_TYPE;
+    }
+
+    @Override
+    public boolean signatureEquals(ElementHandle handle) {
+        if (handle instanceof YamlStructureItem) {
+            return this.equals(handle);
+        }
+        return false;
+    }
+
     @Override
     public String toString() {
         return "" + getPosition() + ": " + getName();
     }
 
+    void setEndMark(long end) {
+        endMark = end;
+    }
+
     public static final class Simple extends YamlStructureItem {
 
         final String name;
-        final long endMark;
         
-        public  Simple(NodeType type, String name, long startMark, long endMark) {
-            super(type, startMark);
+        public  Simple(NodeType type, FileObject fileObject, String name, long startMark, long endMark) {
+            super(type, fileObject, startMark, endMark);
             this.name = name;
-            this.endMark = endMark;
         }
         
         @Override
@@ -125,21 +167,13 @@ public abstract class YamlStructureItem implements StructureItem {
         public List<? extends StructureItem> getNestedItems() {
             return Collections.emptyList();
         }
-
-        @Override
-        public long getEndPosition() {
-            return endMark;
-        }
-        
     }
     
     public static final class Collection extends YamlStructureItem {
         private final List<YamlStructureItem> children = new ArrayList<>();
-        private long endMark;
         
-        public  Collection(NodeType type, long startMark) {
-            super(type, startMark);
-            this.endMark = startMark;
+        public  Collection(NodeType type, FileObject fileObject, long startMark) {
+            super(type, fileObject, startMark, startMark);
         }
         
         @Override
@@ -156,19 +190,10 @@ public abstract class YamlStructureItem implements StructureItem {
             return children;
         }
         
-        @Override
-        public long getEndPosition() {
-            return endMark;
-        }
-
         public void add(YamlStructureItem item) {
-            endMark = item.getEndPosition();
+            setEndMark(item.getEndPosition());
             children.add(item);
         }
-        
-        public void setEndMark(int end) {
-            endMark = end;
-        }
     }
     
     public static final class MapEntry extends YamlStructureItem {
@@ -176,7 +201,7 @@ public abstract class YamlStructureItem implements StructureItem {
         final YamlStructureItem valueItem;
 
         public  MapEntry(YamlStructureItem keyItem, YamlStructureItem valueItem) {
-            super(NodeType.MAPPING, keyItem.startMark);
+            super(NodeType.MAPPING, keyItem.fileObject, keyItem.startMark, valueItem.endMark);
             this.keyItem = keyItem;
             this.valueItem = valueItem;
         }


---------------------------------------------------------------------
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