/* * Copyright 2015 Nokia Solutions and Networks * Licensed under the Apache License, Version 2.0, * see license.txt file for details. */ package org.robotframework.ide.eclipse.main.plugin.tableeditor.source; import static com.google.common.collect.Iterables.filter; import static com.google.common.collect.Iterables.transform; import static com.google.common.collect.Lists.newArrayList; import java.util.Iterator; import java.util.List; import org.eclipse.core.resources.IMarker; import org.eclipse.core.runtime.CoreException; import org.eclipse.jface.text.Position; import org.eclipse.jface.text.contentassist.ContentAssistEvent; import org.eclipse.jface.text.contentassist.ICompletionListener; import org.eclipse.jface.text.contentassist.ICompletionListenerExtension; import org.eclipse.jface.text.contentassist.ICompletionListenerExtension2; import org.eclipse.jface.text.contentassist.ICompletionProposal; import org.eclipse.jface.text.quickassist.IQuickAssistInvocationContext; import org.eclipse.jface.text.quickassist.IQuickAssistProcessor; import org.eclipse.jface.text.source.Annotation; import org.eclipse.jface.text.source.ISourceViewer; import org.eclipse.jface.text.source.SourceViewer; import org.eclipse.swt.widgets.Display; import org.eclipse.ui.IMarkerResolution; import org.eclipse.ui.texteditor.MarkerAnnotation; import org.robotframework.ide.eclipse.main.plugin.model.RobotSuiteFile; import org.robotframework.ide.eclipse.main.plugin.project.build.RobotProblem; import org.robotframework.ide.eclipse.main.plugin.project.build.causes.IProblemCause; import org.robotframework.ide.eclipse.main.plugin.project.build.fix.ProjectsFixesGenerator; import org.robotframework.ide.eclipse.main.plugin.project.build.fix.RedSuiteMarkerResolution; import org.robotframework.ide.eclipse.main.plugin.project.build.fix.RedXmlConfigMarkerResolution; import org.robotframework.ide.eclipse.main.plugin.tableeditor.source.assist.RedCompletionProposal; import org.robotframework.ide.eclipse.main.plugin.tableeditor.source.assist.RedCompletionProposalAdapter; import com.google.common.base.Function; import com.google.common.base.Predicates; import com.google.common.collect.Range; /** * @author Michal Anglart */ public class SuiteSourceQuickAssistProcessor implements IQuickAssistProcessor, ICompletionListener, ICompletionListenerExtension, ICompletionListenerExtension2 { private final RobotSuiteFile suiteModel; private final SourceViewer sourceViewer; public SuiteSourceQuickAssistProcessor(final RobotSuiteFile fileModel, final ISourceViewer sourceViewer) { this.suiteModel = fileModel; this.sourceViewer = (SourceViewer) sourceViewer; } @Override public String getErrorMessage() { return null; } @Override public boolean canFix(final Annotation annotation) { if (annotation instanceof MarkerAnnotation) { try { final IMarker marker = ((MarkerAnnotation) annotation).getMarker(); if (RobotProblem.TYPE_ID.equals(marker.getType())) { final IProblemCause cause = ProjectsFixesGenerator.getCause(marker); return cause != null && cause.hasResolution(); } } catch (final CoreException e) { return false; } } return false; } @Override public boolean canAssist(final IQuickAssistInvocationContext invocationContext) { return false; } private static Position getPosition(final MarkerAnnotation annotation) { try { final Integer start = (Integer) annotation.getMarker().getAttribute(IMarker.CHAR_START); final Integer end = (Integer) annotation.getMarker().getAttribute(IMarker.CHAR_END); return new Position(start, end - start); } catch (final CoreException e) { return null; } } @Override public ICompletionProposal[] computeQuickAssistProposals(final IQuickAssistInvocationContext invocationContext) { final Iterator<?> annotations = invocationContext.getSourceViewer() .getAnnotationModel() .getAnnotationIterator(); while (annotations.hasNext()) { final Annotation annotation = (Annotation) annotations.next(); if (annotation instanceof MarkerAnnotation) { final IMarker marker = ((MarkerAnnotation) annotation).getMarker(); try { if (RobotProblem.TYPE_ID.equals(marker.getType())) { final Position position = getPosition((MarkerAnnotation) annotation); final IProblemCause cause = ProjectsFixesGenerator.getCause(marker); if (cause.hasResolution() && isInvokedWithinAnnotationPosition(invocationContext, position)) { final List<ICompletionProposal> proposals = computeRobotProblemsAssistants( invocationContext, marker, cause); return proposals.isEmpty() ? null : proposals.toArray(new ICompletionProposal[0]); } } } catch (final CoreException e) { return null; } } } return null; } private List<ICompletionProposal> computeRobotProblemsAssistants( final IQuickAssistInvocationContext invocationContext, final IMarker marker, final IProblemCause cause) { final List<? extends IMarkerResolution> fixers = cause.createFixers(marker); final Iterable<RedSuiteMarkerResolution> suiteFixers = filter(fixers, RedSuiteMarkerResolution.class); final Iterable<RedXmlConfigMarkerResolution> redXmlFixers = filter(fixers, RedXmlConfigMarkerResolution.class); final Iterable<ICompletionProposal> suiteRepairProposals = filter( transform(suiteFixers, new Function<RedSuiteMarkerResolution, ICompletionProposal>() { @Override public ICompletionProposal apply(final RedSuiteMarkerResolution resolution) { return resolution.asContentProposal(marker, invocationContext.getSourceViewer().getDocument(), suiteModel).orElse(null); } }), Predicates.notNull()); final Iterable<ICompletionProposal> redXmlRepairProposals = filter( transform(redXmlFixers, new Function<RedXmlConfigMarkerResolution, ICompletionProposal>() { @Override public ICompletionProposal apply(final RedXmlConfigMarkerResolution resolution) { return resolution.asContentProposal(marker); } }), Predicates.notNull()); final List<ICompletionProposal> proposals = newArrayList(redXmlRepairProposals); proposals.addAll(newArrayList(suiteRepairProposals)); return proposals; } private boolean isInvokedWithinAnnotationPosition(final IQuickAssistInvocationContext invocationContext, final Position annotationPosition) { return annotationPosition != null && Range .closed(annotationPosition.getOffset(), annotationPosition.getOffset() + annotationPosition.getLength()) .contains(invocationContext.getOffset()); } @Override public void applied(final ICompletionProposal proposal) { // this method is called also for processors from which the proposal was not chosen // hence canReopenAssistantProgramatically is holding information which proccessor // is able to open proposals after accepting if (shouldActivateAssist(proposal)) { Display.getCurrent().asyncExec(new Runnable() { @Override public void run() { sourceViewer.doOperation(ISourceViewer.CONTENTASSIST_PROPOSALS); } }); } } private boolean shouldActivateAssist(final ICompletionProposal proposal) { return proposal instanceof RedCompletionProposal && ((RedCompletionProposal) proposal).shouldActivateAssitantAfterAccepting() || proposal instanceof RedCompletionProposalAdapter && ((RedCompletionProposalAdapter) proposal).shouldActivateAssitantAfterAccepting(); } @Override public void assistSessionEnded(final ContentAssistEvent event) { // nothing to do } @Override public void assistSessionStarted(final ContentAssistEvent event) { // nothing to do } @Override public void assistSessionRestarted(final ContentAssistEvent event) { // nothing to do } @Override public void selectionChanged(final ICompletionProposal proposal, final boolean smartToggle) { // nothing to do } }