package com.technophobia.substeps.document.content.view.hover; import java.util.List; import java.util.regex.Pattern; import org.eclipse.core.resources.IProject; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.IInformationControl; import org.eclipse.jface.text.IInformationControlCreator; import org.eclipse.jface.text.IRegion; import org.eclipse.jface.text.ITextHover; import org.eclipse.jface.text.ITextHoverExtension; import org.eclipse.jface.text.ITextViewer; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Shell; import com.technophobia.eclipse.javadoc.ProjectJavaDocLocator; import com.technophobia.substeps.FeatureEditorPlugin; import com.technophobia.substeps.document.content.view.hover.model.HoverModel; import com.technophobia.substeps.document.content.view.hover.model.StepImplementationHoverModel; import com.technophobia.substeps.document.content.view.hover.model.SubstepHoverModel; import com.technophobia.substeps.model.ParentStep; import com.technophobia.substeps.model.StepImplementation; import com.technophobia.substeps.model.Syntax; import com.technophobia.substeps.supplier.DefaultMutableItemContainer; import com.technophobia.substeps.supplier.MutableSupplier; import com.technophobia.substeps.supplier.Supplier; public class SubstepsTextHover implements ITextHover, ITextHoverExtension { private final Supplier<IProject> currentProjectSupplier; private final ProjectJavaDocLocator<StepImplementation> javadocLocator; public SubstepsTextHover(final Supplier<IProject> currentProjectSupplier, final ProjectJavaDocLocator<StepImplementation> javadocLocator) { this.currentProjectSupplier = currentProjectSupplier; this.javadocLocator = javadocLocator; } @Override public String getHoverInfo(final ITextViewer textViewer, final IRegion hoverRegion) { final IProject project = currentProject(); final String line = lineFor(textViewer, hoverRegion); final Syntax syntax = FeatureEditorPlugin.instance().syntaxFor(project); final HoverModel hoverModel = hoverModelAtLine(line, syntax); if (hoverModel != null) { return hoverModel.serializeToString(); } return null; } private HoverModel hoverModelAtLine(final String line, final Syntax syntax) { final List<ParentStep> substeps = substepsForLine(line, syntax); if (substeps != null && !substeps.isEmpty()) { return new SubstepHoverModel(substeps.get(0)); } final StepImplementation stepImplementation = findStepImpl(line, syntax); if (stepImplementation != null) { return new StepImplementationHoverModel(stepImplementation, currentProject(), javadocLocator); } return null; } private StepImplementation findStepImpl(final String line, final Syntax syntax) { final List<StepImplementation> stepImplementations = syntax.getStepImplementations(); for (final StepImplementation stepImplementation : stepImplementations) { final String stepImplValue = stepImplementation.getValue(); if (Pattern.matches(stepImplValue, line)) { return stepImplementation; } } return null; } @Override public IRegion getHoverRegion(final ITextViewer textViewer, final int offset) { final IDocument document = textViewer.getDocument(); return getLineRegionAtOffset(document, offset); } @Override public IInformationControlCreator getHoverControlCreator() { return new IInformationControlCreator() { @Override public IInformationControl createInformationControl(final Shell parent) { return new SubstepsHoverControl(parent); } }; } private List<ParentStep> substepsForLine(final String line, final Syntax syntax) { return syntax.getSubStepsMap().get(line); } private String lineFor(final ITextViewer textViewer, final IRegion region) { try { return textViewer.getDocument().get(region.getOffset(), region.getLength()).trim(); } catch (final BadLocationException e) { FeatureEditorPlugin.instance().error( "Couldn't get line at region (" + region.getOffset() + ", " + region.getLength() + ")"); } return null; } private IRegion getLineRegionAtOffset(final IDocument document, final int offset) { try { return document.getLineInformationOfOffset(offset); } catch (final BadLocationException ex) { FeatureEditorPlugin.instance().error("Could not find line for offset " + offset, ex); } return null; } private IProject currentProject() { final MutableSupplier<IProject> projectSupplier = new DefaultMutableItemContainer<IProject>(); Display.getDefault().syncExec(new Runnable() { @Override public void run() { projectSupplier.set(currentProjectSupplier.get()); } }); return projectSupplier.get(); } }