/* * Copyright 2011-present Greg 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.editor; import com.intellij.codeInsight.daemon.RelatedItemLineMarkerInfo; import com.intellij.codeInsight.daemon.RelatedItemLineMarkerProvider; import com.intellij.codeInsight.navigation.NavigationGutterIconBuilder; import com.intellij.openapi.actionSystem.ActionManager; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.keymap.KeymapUtil; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.NavigatablePsiElement; import com.intellij.psi.PsiElement; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.containers.ContainerUtil; import gnu.trove.THashSet; import org.intellij.grammar.BnfIcons; import org.intellij.grammar.KnownAttribute; import org.intellij.grammar.generator.ParserGeneratorUtil; import org.intellij.grammar.generator.RuleGraphHelper; import org.intellij.grammar.java.JavaHelper; import org.intellij.grammar.psi.BnfExpression; import org.intellij.grammar.psi.BnfRule; import org.intellij.grammar.psi.impl.GrammarUtil; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Set; /** * @author gregsh */ public class BnfRuleLineMarkerProvider extends RelatedItemLineMarkerProvider { @Override public void collectNavigationMarkers(List<PsiElement> elements, Collection<? super RelatedItemLineMarkerInfo> result, boolean forNavigation) { Set<PsiElement> visited = forNavigation ? new THashSet<>() : null; for (PsiElement element : elements) { PsiElement parent = element.getParent(); boolean isRuleId = parent instanceof BnfRule && (forNavigation || element == ((BnfRule)parent).getId()); if (!(isRuleId || forNavigation && element instanceof BnfExpression)) continue; List<PsiElement> items = new ArrayList<>(); NavigatablePsiElement method = getMethod(element); if (method != null && (!forNavigation || visited.add(method))) { items.add(method); } boolean hasPSI = false; if (isRuleId) { BnfRule rule = RuleGraphHelper.getSynonymTargetOrSelf((BnfRule)parent); if (RuleGraphHelper.hasPsiClass(rule)) { hasPSI = true; JavaHelper javaHelper = JavaHelper.getJavaHelper(rule); for (String className : new String[]{ParserGeneratorUtil.getQualifiedRuleClassName(rule, false), ParserGeneratorUtil.getQualifiedRuleClassName(rule, true)}) { NavigatablePsiElement aClass = javaHelper.findClass(className); if (aClass != null && (!forNavigation || visited.add(aClass))) { items.add(aClass); } } } } if (!items.isEmpty()) { AnAction action = ActionManager.getInstance().getAction("GotoRelated"); String tooltipAd = ""; String popupTitleAd = ""; if (action != null) { String shortcutText = KeymapUtil.getFirstKeyboardShortcutText(action); String actionText = StringUtil.isEmpty(shortcutText) ? "'" + action.getTemplatePresentation().getText() + "' action" : shortcutText; tooltipAd = "\nGo to sub-expression code via " + actionText; popupTitleAd = " (for sub-expressions use " + actionText + ")"; } String title = "parser " + (hasPSI ? "and PSI " : "") + "code"; NavigationGutterIconBuilder<PsiElement> builder = NavigationGutterIconBuilder.create(BnfIcons.RELATED_METHOD). setTargets(items). setTooltipText("Click to navigate to "+title + tooltipAd). setPopupTitle(StringUtil.capitalize(title) + popupTitleAd); result.add(builder.createLineMarkerInfo(element)); } } } @Nullable private static NavigatablePsiElement getMethod(PsiElement element) { BnfRule rule = PsiTreeUtil.getParentOfType(element, BnfRule.class); if (rule == null) return null; String parserClass = ParserGeneratorUtil.getAttribute(rule, KnownAttribute.PARSER_CLASS); if (StringUtil.isEmpty(parserClass)) return null; JavaHelper helper = JavaHelper.getJavaHelper(element); List<NavigatablePsiElement> methods = helper.findClassMethods( parserClass, JavaHelper.MethodType.STATIC, GrammarUtil.getMethodName(rule, element), -1); return ContainerUtil.getFirstItem(methods); } }