com.intellij.lang.jsgraphql.endpoint.doc.psi.JSGraphQLEndpointDocPsiUtil.java Source code

Java tutorial

Introduction

Here is the source code for com.intellij.lang.jsgraphql.endpoint.doc.psi.JSGraphQLEndpointDocPsiUtil.java

Source

/**
 * Copyright (c) 2015-present, Jim Kynde Meyer
 * All rights reserved.
 * <p>
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */
package com.intellij.lang.jsgraphql.endpoint.doc.psi;

import java.util.Collections;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;

import org.apache.commons.lang.StringUtils;

import com.google.common.collect.Lists;
import com.intellij.lang.injection.InjectedLanguageManager;
import com.intellij.lang.jsgraphql.endpoint.psi.JSGraphQLEndpointFieldDefinition;
import com.intellij.lang.jsgraphql.endpoint.psi.JSGraphQLEndpointNamedTypeDefinition;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiComment;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiLanguageInjectionHost;
import com.intellij.psi.PsiWhiteSpace;
import com.intellij.psi.util.PsiTreeUtil;

public class JSGraphQLEndpointDocPsiUtil {

    /**
     * Gets the values of other documentation tags that are immediate siblings
     *
     * @param comment the comment to use as a staring point
     */
    public static List<String> getOtherDocTagValues(PsiComment comment) {

        final Project project = comment.getProject();
        final InjectedLanguageManager injectedLanguageManager = InjectedLanguageManager.getInstance(project);

        final List<PsiComment> siblings = Lists.newArrayList();
        getDocumentationCommentSiblings(comment, siblings, PsiElement::getPrevSibling);
        getDocumentationCommentSiblings(comment, siblings, PsiElement::getNextSibling);

        final List<String> values = Lists.newArrayList();

        for (PsiComment sibling : siblings) {
            if (sibling instanceof PsiLanguageInjectionHost) {
                List<Pair<PsiElement, TextRange>> injected = injectedLanguageManager.getInjectedPsiFiles(sibling);
                if (injected != null) {
                    for (Pair<PsiElement, TextRange> pair : injected) {
                        PsiFile injectedFile = pair.first.getContainingFile();
                        final JSGraphQLEndpointDocTag tag = PsiTreeUtil.findChildOfType(injectedFile,
                                JSGraphQLEndpointDocTag.class);
                        if (tag != null && tag.getDocValue() != null) {
                            values.add(tag.getDocValue().getText());
                        }
                    }
                }
            }
        }
        return values;
    }

    private static void getDocumentationCommentSiblings(PsiComment comment, List<PsiComment> comments,
            Function<PsiElement, PsiElement> step) {
        PsiElement element = step.apply(comment);
        while (element != null) {
            if (element instanceof PsiWhiteSpace) {
                element = step.apply(element);
            } else if (isDocumentationComment(element)) {
                comments.add((PsiComment) element);
                element = step.apply(element);
            } else {
                break;
            }
        }
    }

    /**
     * Gets whether the specified comment is considered documentation, i.e. that it's placed directly above a type or field definition
     */
    public static boolean isDocumentationComment(PsiElement element) {
        if (element instanceof PsiComment) {
            PsiElement next = element.getNextSibling();
            while (next != null) {
                final boolean isWhiteSpace = next instanceof PsiWhiteSpace;
                if (next instanceof PsiComment || isWhiteSpace) {
                    if (isWhiteSpace && StringUtils.countMatches(next.getText(), "\n") > 1) {
                        // a blank line before the next element, so this comment is not directly above it
                        break;
                    }
                    next = next.getNextSibling();
                } else {
                    break;
                }
            }
            if (next instanceof JSGraphQLEndpointFieldDefinition
                    || next instanceof JSGraphQLEndpointNamedTypeDefinition) {
                return true;
            }
        }
        return false;
    }

    /**
     * Gets the PSI comment that starts the documentation for the specified element, or <code>null</code> if no documentation is available
     */
    public static PsiComment getDocumentationStartElement(PsiElement element) {
        final PsiComment comment = PsiTreeUtil.getPrevSiblingOfType(element, PsiComment.class);
        if (isDocumentationComment(comment)) {
            final List<PsiComment> siblings = Lists.newArrayList(comment);
            getDocumentationCommentSiblings(comment, siblings, PsiElement::getPrevSibling);
            Collections.reverse(siblings);
            return siblings.get(0);
        }
        return null;
    }

    /**
     * Gets the text of the continuous comments placed directly above the specified element
     * @param element element whose previous siblings are enumerated and included if they're documentation comments
     * @return the combined text of the documentation comments, preserving line breaks, or <code>null</code> if no documentation is available
     */
    public static String getDocumentation(PsiElement element) {
        final PsiComment comment = PsiTreeUtil.getPrevSiblingOfType(element, PsiComment.class);
        if (isDocumentationComment(comment)) {
            final List<PsiComment> siblings = Lists.newArrayList(comment);
            getDocumentationCommentSiblings(comment, siblings, PsiElement::getPrevSibling);
            Collections.reverse(siblings);
            return siblings.stream().map(c -> StringUtils.stripStart(c.getText(), "# "))
                    .collect(Collectors.joining("\n"));
        }
        return null;
    }
}