All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.netbeans.modules.html.custom.CustomHtmlExtension Maven / Gradle / Ivy

/*
 * 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.html.custom;

import org.netbeans.modules.html.custom.hints.CheckerElementVisitor;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import org.netbeans.api.editor.mimelookup.MimeRegistration;
import org.netbeans.api.editor.mimelookup.MimeRegistrations;
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.OffsetRange;
import org.netbeans.modules.csl.api.RuleContext;
import org.netbeans.modules.html.custom.conf.Configuration;
import org.netbeans.modules.html.custom.conf.Tag;
import org.netbeans.modules.html.custom.hints.CustomElementHint;
import org.netbeans.modules.html.editor.api.gsf.HtmlExtension;
import org.netbeans.modules.html.editor.api.gsf.HtmlParserResult;
import org.netbeans.modules.html.editor.lib.api.HtmlSource;
import org.netbeans.modules.html.editor.lib.api.SyntaxAnalyzerResult;
import org.netbeans.modules.html.editor.lib.api.elements.Attribute;
import org.netbeans.modules.html.editor.lib.api.elements.Element;
import org.netbeans.modules.html.editor.lib.api.elements.ElementType;
import org.netbeans.modules.html.editor.lib.api.elements.ElementUtils;
import org.netbeans.modules.html.editor.lib.api.elements.Named;
import org.netbeans.modules.html.editor.lib.api.elements.Node;
import org.netbeans.modules.html.editor.lib.api.elements.OpenTag;
import org.netbeans.modules.parsing.api.Snapshot;
import org.netbeans.spi.editor.completion.CompletionItem;
import org.openide.filesystems.FileObject;
import org.openide.util.Pair;

/**
 *
 * @author marek
 */
@MimeRegistrations({
    @MimeRegistration(mimeType = "text/html", service = HtmlExtension.class)
})
public class CustomHtmlExtension extends HtmlExtension {

    private Pair cache;

    @Override
    public boolean isCustomTag(Named element, HtmlSource source) {
        return getConfiguration(source).getTagsNames().contains(element.name().toString());
    }

    @Override
    public boolean isCustomAttribute(Attribute attribute, HtmlSource source) {
        return getConfiguration(source).getAttributesNames().contains(attribute.name().toString());
    }

    private Configuration getConfiguration(HtmlSource source) {
        if (cache == null) {
            //no cache - create
            FileObject sourceFileObject = source.getSourceFileObject();
            Project project = sourceFileObject == null ? null : FileOwnerQuery.getOwner(sourceFileObject);  
            Configuration conf = project == null ? Configuration.EMPTY : Configuration.get(project);
            cache = Pair.of(source, conf);
            return cache.second();
        } else {
            //check if the current source is the cached one
            if (source == cache.first()) {
                //yes, just return cached conf
                return cache.second();
            } else {
                //no, reset cache and try again
                cache = null;
                return getConfiguration(source);
            }
        }
    }

    @Override
    public void computeSuggestions(HintsProvider.HintsManager manager, RuleContext context, List hints, int caretOffset) {
        HtmlParserResult result = (HtmlParserResult) context.parserResult;
        Node root = result.root(SyntaxAnalyzerResult.FILTERED_CODE_NAMESPACE);
        Snapshot snapshot = result.getSnapshot();
        int embeddedCaretOffset = snapshot.getEmbeddedOffset(caretOffset);
        Element found = ElementUtils.findByPhysicalRange(root, embeddedCaretOffset, false);
        if (found != null) {
            switch (found.type()) {
                case OPEN_TAG:
                case CLOSE_TAG:
                    Named named = (Named) found;
                    String elementName = named.name().toString();
                    Configuration conf = Configuration.get(snapshot.getSource().getFileObject());
                    if (conf.getTagsNames().contains(elementName)) {
                        //custom element
                        hints.add(new CustomElementHint(elementName, context, new OffsetRange(snapshot.getOriginalOffset(found.from()), snapshot.getOriginalOffset(found.to()))));

                    }

                //TODO add check + fix for missing required attributes
            }
        }

    }

    @Override
    public void computeErrors(HintsProvider.HintsManager manager, final RuleContext context, final List hints, List unhandled) {
        HtmlParserResult result = (HtmlParserResult) context.parserResult;
        Node root = result.root(SyntaxAnalyzerResult.FILTERED_CODE_NAMESPACE);
        final Snapshot snapshot = result.getSnapshot();
        final Configuration conf = Configuration.get(snapshot.getSource().getFileObject());
        ElementUtils.visitChildren(root, CheckerElementVisitor.getChecker(context, conf, snapshot, hints));
    }

    @Override
    public List completeOpenTags(CompletionContext context) {
        List items = new ArrayList<>();
        FileObject file = context.getResult().getSnapshot().getSource().getFileObject();
        Configuration conf = Configuration.EMPTY;
        if (file != null) {
           conf = Configuration.get(file);
        }
        for (Tag t : conf.getTags()) {
            String tagName = t.getName();
            if (tagName.startsWith(context.getPrefix())) {
                items.add(new CustomTagCompletionItem(t, context.getCCItemStartOffset()));
            }
        }
        return items;
    }

    @Override
    public List completeAttributes(CompletionContext context) {
        Element node = context.getCurrentNode();
        FileObject fileObject = context.getResult().getSnapshot().getSource().getFileObject();
        if (node.type() != ElementType.OPEN_TAG || fileObject == null) {
            return Collections.emptyList();
        }
        OpenTag ot = (OpenTag) node;
        List items = new ArrayList<>();
        Configuration conf = Configuration.get(fileObject);
        String tagName = ((OpenTag) node).name().toString();
        Tag t = conf.getTag(tagName);
        if (t != null) {
            //complete attribute specific for the element
            for (org.netbeans.modules.html.custom.conf.Attribute a : t.getAttributes()) {
                //do not complete already existing attributes
                String aName = a.getName();
                if (ot.getAttribute(aName) == null) {
                    if (aName.startsWith(context.getPrefix())) {
                        items.add(new CustomAttributeCompletionItem(a, context.getCCItemStartOffset()));
                    }
                }
            }
        }

        //complete global attributes
        for (org.netbeans.modules.html.custom.conf.Attribute a : conf.getAttributes()) {
            Collection contexts = a.getContexts();
            //complete either contextfree attribute or if the current element is the attribute context
            if (contexts.isEmpty() || contexts.contains(tagName)) {
                //do not complete already existing attributes
                String aName = a.getName();
                if (ot.getAttribute(aName) == null) {
                    if (aName.startsWith(context.getPrefix())) {
                        items.add(new CustomAttributeCompletionItem(a, context.getCCItemStartOffset()));
                    }
                }
            }
        }

        return items;
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy