package com.siberika.idea.pascal.editor;
import com.intellij.codeHighlighting.Pass;
import com.intellij.codeInsight.daemon.DaemonCodeAnalyzerSettings;
import com.intellij.codeInsight.daemon.GutterIconNavigationHandler;
import com.intellij.codeInsight.daemon.LineMarkerInfo;
import com.intellij.codeInsight.daemon.LineMarkerProvider;
import com.intellij.codeInsight.daemon.impl.LineMarkersPass;
import com.intellij.icons.AllIcons;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.colors.EditorColorsManager;
import com.intellij.openapi.editor.markup.GutterIconRenderer;
import com.intellij.psi.PsiElement;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.ConstantFunction;
import com.siberika.idea.pascal.PascalBundle;
import com.siberika.idea.pascal.ide.actions.GotoSuper;
import com.siberika.idea.pascal.ide.actions.SectionToggle;
import com.siberika.idea.pascal.lang.psi.PasEntityScope;
import com.siberika.idea.pascal.lang.psi.PasInvalidScopeException;
import com.siberika.idea.pascal.lang.psi.PasUnitInterface;
import com.siberika.idea.pascal.lang.psi.PasUsesClause;
import com.siberika.idea.pascal.lang.psi.PascalNamedElement;
import com.siberika.idea.pascal.lang.psi.PascalStructType;
import com.siberika.idea.pascal.lang.psi.impl.PasExportedRoutineImpl;
import com.siberika.idea.pascal.lang.psi.impl.PasRoutineImplDeclImpl;
import com.siberika.idea.pascal.lang.psi.impl.PascalRoutineImpl;
import com.siberika.idea.pascal.util.EditorUtil;
import com.siberika.idea.pascal.util.PsiUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.event.MouseEvent;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
/**
* Author: George Bakhtadze
* Date: 05/09/2013
*/
public class PascalLineMarkerProvider implements LineMarkerProvider {
public static final Logger LOG = Logger.getInstance(PascalLineMarkerProvider.class.getName());
private final DaemonCodeAnalyzerSettings myDaemonSettings;
private final EditorColorsManager myColorsManager;
public PascalLineMarkerProvider(DaemonCodeAnalyzerSettings daemonSettings, EditorColorsManager colorsManager) {
myDaemonSettings = daemonSettings;
myColorsManager = colorsManager;
}
private void collectNavigationMarkers(@NotNull PsiElement element, Collection<? super LineMarkerInfo> result) {
boolean impl = true;
PsiElement target = null;
if (element instanceof PasExportedRoutineImpl) {
target = SectionToggle.getRoutineTarget((PasExportedRoutineImpl) element);
} else if (element instanceof PasRoutineImplDeclImpl) {
target = SectionToggle.getRoutineTarget((PasRoutineImplDeclImpl) element);
impl = false;
} else if (element instanceof PasUsesClause) {
target = SectionToggle.getUsesTarget((PasUsesClause) element);
impl = PsiTreeUtil.getParentOfType(element, PasUnitInterface.class) != null;
}
if (PsiUtil.isElementUsable(target)) {
result.add(createLineMarkerInfo(element, impl ? AllIcons.Gutter.ImplementedMethod : AllIcons.Gutter.ImplementingMethod,
msg("navigate.title.toggle.section"), getHandler(msg("navigate.title.toggle.section"), Collections.singletonList(target))));
}
// Goto super
if (element instanceof PascalNamedElement) {
PascalNamedElement namedElement = (PascalNamedElement) element;
Collection<PasEntityScope> supers = GotoSuper.retrieveGotoSuperTargets(namedElement.getNameIdentifier());
if (!supers.isEmpty()) {
result.add(createLineMarkerInfo((PasEntityScope) element, AllIcons.Gutter.OverridingMethod, msg("navigate.title.goto.super"),
getHandler(msg("navigate.title.goto.super"), supers)));
}
}
}
private String msg(String key) {
return PascalBundle.message(key);
}
static <T extends PsiElement> LineMarkerInfo<T> createLineMarkerInfo(@NotNull T element, Icon icon, final String tooltip,
@NotNull GutterIconNavigationHandler<T> handler) {
return new LineMarkerInfo<T>(element, element.getTextRange(),
icon, Pass.UPDATE_OVERRIDDEN_MARKERS,
new ConstantFunction<T, String>(tooltip), handler,
GutterIconRenderer.Alignment.RIGHT);
}
@Nullable
@Override
public LineMarkerInfo getLineMarkerInfo(@NotNull PsiElement element) {
if (element instanceof PasRoutineImplDeclImpl) {
if (myDaemonSettings.SHOW_METHOD_SEPARATORS) {
return LineMarkersPass.createMethodSeparatorLineMarker(element, myColorsManager);
}
}
return null;
}
@Override
public void collectSlowLineMarkers(@NotNull List<PsiElement> elements, @NotNull Collection<LineMarkerInfo> result) {
try {
for (PsiElement element : elements) {
if ((element instanceof PascalRoutineImpl) || (element instanceof PascalStructType) || (element instanceof PasUsesClause)) {
collectNavigationMarkers(element, result);
}
}
} catch (PasInvalidScopeException e) {
e.printStackTrace();
}
}
static <T extends PsiElement> GutterIconNavigationHandler<T> getHandler(final String title, @NotNull final Collection<T> targets) {
return new GutterIconNavigationHandler<T>() {
@Override
public void navigate(MouseEvent e, PsiElement elt) {
EditorUtil.navigateTo(e, title, null, targets);
}
};
}
}