Package org.intellij.grammar

Source Code of org.intellij.grammar.BnfDocumentationProvider

/*
* Copyright 2011-2014 Gregory Shrago
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.intellij.grammar;

import com.intellij.codeInsight.documentation.DocumentationComponent;
import com.intellij.codeInsight.documentation.DocumentationManager;
import com.intellij.lang.documentation.DocumentationProvider;
import com.intellij.openapi.application.AccessToken;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.popup.JBPopup;
import com.intellij.openapi.util.Getter;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.wm.ToolWindow;
import com.intellij.openapi.wm.ToolWindowId;
import com.intellij.openapi.wm.ToolWindowManager;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiManager;
import com.intellij.psi.PsiNamedElement;
import com.intellij.psi.impl.source.tree.LeafPsiElement;
import com.intellij.ui.ColorUtil;
import com.intellij.ui.JBColor;
import com.intellij.ui.content.Content;
import com.intellij.ui.popup.AbstractPopup;
import com.intellij.util.ObjectUtils;
import com.intellij.util.PairConsumer;
import org.intellij.grammar.analysis.BnfFirstNextAnalyzer;
import org.intellij.grammar.generator.BnfConstants;
import org.intellij.grammar.generator.ExpressionHelper;
import org.intellij.grammar.generator.ParserGeneratorUtil;
import org.intellij.grammar.generator.RuleGraphHelper;
import org.intellij.grammar.psi.BnfAttr;
import org.intellij.grammar.psi.BnfExpression;
import org.intellij.grammar.psi.BnfFile;
import org.intellij.grammar.psi.BnfRule;
import org.jetbrains.annotations.Nullable;

import java.util.*;

/**
* @author gregsh
*/
public class BnfDocumentationProvider implements DocumentationProvider {

  @Nullable
  public String getQuickNavigateInfo(final PsiElement element, PsiElement originalElement) {
    return null;
  }

  @Nullable
  public List<String> getUrlFor(final PsiElement element, final PsiElement originalElement) {
    return null;
  }

  @Nullable
  public String generateDoc(final PsiElement element, final PsiElement originalElement) {
    if (element instanceof BnfRule) {
      final BnfRule rule = (BnfRule)element;
      BnfFirstNextAnalyzer analyzer = new BnfFirstNextAnalyzer();
      Set<String> first = analyzer.asStrings(analyzer.calcFirst(rule));
      Set<String> next = analyzer.asStrings(analyzer.calcNext(rule).keySet());

      StringBuilder docBuilder = new StringBuilder();
      String[] firstS = first.toArray(new String[first.size()]);
      Arrays.sort(firstS);
      docBuilder.append("<h1>Starts with:</h1>");
      docBuilder.append("<code>").append(StringUtil.escapeXml(StringUtil.join(firstS, " | "))).append("</code>");

      String[] nextS = next.toArray(new String[next.size()]);
      Arrays.sort(nextS);
      docBuilder.append("<br><h1>Followed by:</h1>");
      docBuilder.append("<code>").append(StringUtil.escapeXml(StringUtil.join(nextS, " | "))).append("</code>");

      BnfFile file = (BnfFile)rule.getContainingFile();
      String recover = file.findAttributeValue(rule, KnownAttribute.RECOVER_WHILE, null);
      if (BnfConstants.RECOVER_AUTO.equals(recover)) {
        docBuilder.append("<br><h1>#auto recovery predicate:</h1>");
        docBuilder.append("<code>");
        docBuilder.append("private ").append(rule.getName()).append("_recover ::= !(");
        boolean f = true;
        for (String s : nextS) {
          if (s.startsWith("-") || s.startsWith("<")) continue;
          if (file.getRule(s) != null) continue;
          if (f) f = false;
          else docBuilder.append(" | ");
          docBuilder.append(StringUtil.escapeXml(s));
        }
        docBuilder.append(")");
        docBuilder.append("</code>");
      }

      final String prefix = docBuilder.toString();
      if (!ParserGeneratorUtil.Rule.isMeta(rule)) {
        updateDocPopup(rule, new Getter<String>() {
          @Override
          public String get() {
            AccessToken accessToken = ApplicationManager.getApplication().acquireReadActionLock();
            try {
              return getUpdatedDocumentation(rule, prefix);
            }
            finally {
              accessToken.finish();
            }
          }
        });
      }
      return prefix;
    }
    else if (element instanceof BnfAttr) {
      KnownAttribute attribute = KnownAttribute.getAttribute(((BnfAttr)element).getName());
      if (attribute != null) return attribute.getDescription();
    }
    return null;
  }

  @Nullable
  public PsiElement getDocumentationElementForLookupItem(final PsiManager psiManager, final Object obj5ect, final PsiElement element) {
    return null;
  }

  @Nullable
  public PsiElement getDocumentationElementForLink(final PsiManager psiManager, final String link, final PsiElement context) {
    return null;
  }

  private static String getUpdatedDocumentation(final BnfRule rule, String prefix) {
    StringBuilder docBuilder = new StringBuilder(prefix);
    BnfFile file = (BnfFile)rule.getContainingFile();
    dumpPriorityTable(docBuilder, rule, file);
    dumpContents(docBuilder, rule, file);
    return docBuilder.toString();
  }

  private static void dumpContents(StringBuilder docBuilder, BnfRule rule, BnfFile file) {
    Map<PsiElement, RuleGraphHelper.Cardinality> map = RuleGraphHelper.getCached(file).getFor(rule);
    Collection<BnfRule> sortedPublicRules = ParserGeneratorUtil.getSortedPublicRules(map.keySet());
    Collection<BnfExpression> sortedTokens = ParserGeneratorUtil.getSortedTokens(map.keySet());
    Collection<LeafPsiElement> sortedExternalRules = ParserGeneratorUtil.getSortedExternalRules(map.keySet());
    if (sortedPublicRules.isEmpty() && sortedTokens.isEmpty()) {
      docBuilder.append("\n<br><h1>Contains no public rules and no tokens</h1>");
    }
    else {
      if (sortedPublicRules.size() > 0) {
        printElements(map, sortedPublicRules, docBuilder.append("\n<br><h1>Contains public rules:</h1>"));
      }
      else {
        docBuilder.append("<h2>Contains no public rules</h2>");
      }
      if (sortedTokens.size() > 0) {
        printElements(map, sortedTokens, docBuilder.append("\n<br><h1>Contains tokens:</h1>"));
      }
      else {
        docBuilder.append("<h2>Contains no tokens</h2>");
      }
    }
    if (!sortedExternalRules.isEmpty()) {
      printElements(map, sortedExternalRules, docBuilder.append("\n<br><h1>Contains external rules:</h1>"));
    }
  }

  private static void dumpPriorityTable(StringBuilder docBuilder, BnfRule rule, BnfFile file) {
    ExpressionHelper.ExpressionInfo expressionInfo = ExpressionHelper.getCached(file).getExpressionInfo(rule);
    if (expressionInfo == null) return;
    final ExpressionHelper.OperatorInfo ruleOperator = expressionInfo.operatorMap.get(rule);

    docBuilder.append("\n<br><h1>Priority table:");
    if (ruleOperator != null) {
      appendColored(docBuilder, " " + ruleOperator.type + "-" + expressionInfo.getPriority(rule));
    }
    docBuilder.append("</h1>");
    expressionInfo.dumpPriorityTable(docBuilder.append("<code><pre>"), new PairConsumer<StringBuilder, ExpressionHelper.OperatorInfo>() {
      @Override
      public void consume(StringBuilder sb, ExpressionHelper.OperatorInfo operatorInfo) {
        if (operatorInfo == ruleOperator) {
          appendColored(sb, operatorInfo);
        }
        else {
          sb.append(operatorInfo);
        }

      }
    }).append("</pre></code>");
  }

  private static void appendColored(StringBuilder sb, Object o) {
    sb.append("<font").append(" color=\"#").append(ColorUtil.toHex(JBColor.BLUE)).append("\">");
    sb.append(o);
    sb.append("</font>");
  }

  public static void printElements(Map<PsiElement, RuleGraphHelper.Cardinality> map,
                                   Collection<? extends PsiElement> collection,
                                   StringBuilder sb) {
    for (PsiElement r : collection) {
      sb.append(" ").append(r instanceof PsiNamedElement? ((PsiNamedElement)r).getName() : r.getText()).
        append(RuleGraphHelper.getCardinalityText(map.get(r)));
    }
  }

  public static void updateDocPopup(final PsiElement element, final Getter<String> docGetter) {
    final Project project = element.getProject();
    ApplicationManager.getApplication().executeOnPooledThread(new Runnable() {
      @Override
      public void run() {
        final String documentation = docGetter.get();
        if (StringUtil.isEmpty(documentation)) return;
        ApplicationManager.getApplication().invokeLater(new Runnable() {
          @Override
          public void run() {
            DocumentationManager documentationManager = DocumentationManager.getInstance(project);
            DocumentationComponent component;
            JBPopup hint = documentationManager.getDocInfoHint();
            if (hint != null) {
              component = (DocumentationComponent)((AbstractPopup)hint).getComponent();
            }
            else if (documentationManager.hasActiveDockedDocWindow()) {
              ToolWindow toolWindow = ToolWindowManager.getInstance(project).getToolWindow(ToolWindowId.DOCUMENTATION);
              Content selectedContent = toolWindow == null? null : toolWindow.getContentManager().getSelectedContent();
              component = selectedContent == null ? null : (DocumentationComponent)selectedContent.getComponent();
            }
            else {
              component = null;
            }
            PsiElement docElement = component == null? null : ObjectUtils.chooseNotNull(component.getElement(), element);
            if (docElement == element) {
              component.setText(documentation, element, false);
            }
          }
        });
      }
    });
  }
}
TOP

Related Classes of org.intellij.grammar.BnfDocumentationProvider

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.