/******************************************************************************* * Copyright Technophobia Ltd 2012 * * This file is part of the Substeps Eclipse Plugin. * * The Substeps Eclipse Plugin is free software: you can redistribute it and/or modify * it under the terms of the Eclipse Public License v1.0. * * The Substeps Eclipse Plugin is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * Eclipse Public License for more details. * * You should have received a copy of the Eclipse Public License * along with the Substeps Eclipse Plugin. If not, see <http://www.eclipse.org/legal/epl-v10.html>. ******************************************************************************/ package com.technophobia.substeps.document.content.assist.feature; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import org.eclipse.core.resources.IResource; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.contentassist.CompletionProposal; import org.eclipse.jface.text.contentassist.ICompletionProposal; import org.eclipse.ui.IWorkbenchPartSite; import com.technophobia.substeps.FeatureEditorPlugin; import com.technophobia.substeps.document.content.assist.CompletionProposalProvider; import com.technophobia.substeps.step.ContextualSuggestionManager; import com.technophobia.substeps.step.Suggestion; /** * Implementation of {@link CompletionProposalProvider} that looks up all class * files in the current {@link IJavaProject}, locates their step definition * methods (if any), and filters out non-applicable ones, returning the * remainder as {@link ICompletionProposal}s * * @author sforbes * */ public class StepImplementationProposalProvider implements CompletionProposalProvider { private final IWorkbenchPartSite site; private final ContextualSuggestionManager suggestionManager; public StepImplementationProposalProvider(final IWorkbenchPartSite site, final ContextualSuggestionManager suggestionManager) { this.site = site; this.suggestionManager = suggestionManager; } @Override public ICompletionProposal[] get(final IDocument document, final int offset) { // final Syntax syntax = siteToSyntaxTransformer.to(site); final IResource resource = activeEditorResource(); final List<Suggestion> suggestions = suggestionManager.suggestionsFor(resource); Collections.sort(suggestions); return createCompletionsForSuggestions(document, offset, suggestions); } /** * Find the current resource for the active editor * * @return */ private IResource activeEditorResource() { return (IResource) site.getWorkbenchWindow().getActivePage().getActiveEditor().getEditorInput() .getAdapter(IResource.class); } /** * Filter out non-applicable steps, based on the current position of the * cursor in the document. Convert the remainder to * {@link ICompletionProposal} * * @param document * The currently edited document * @param offset * position in the document of the cursor * @param suggestions * All suggestions * @return Applicable {@link ICompletionProposal}s */ private ICompletionProposal[] createCompletionsForSuggestions(final IDocument document, final int offset, final Collection<Suggestion> suggestions) { final Collection<ICompletionProposal> completionProposals = createPopulatedCompletionsForSuggestions(document, offset, suggestions); if (completionProposals.isEmpty()) { completionProposals.add(new NoCompletionsProposal()); } return completionProposals.toArray(new ICompletionProposal[completionProposals.size()]); } private Collection<ICompletionProposal> createPopulatedCompletionsForSuggestions(final IDocument document, final int offset, final Collection<Suggestion> suggestions) { final List<ICompletionProposal> completionProposals = new ArrayList<ICompletionProposal>(); // filter the list based on current text final String startOfLine = getLineUpTo(document, offset); final Set<Suggestion> existingSuggestions = new HashSet<Suggestion>(); for (final Suggestion suggestion : suggestions) { final String suggestionText = suggestion.getText(); if (!isExistingSuggestion(suggestionText, existingSuggestions)) { if (startOfLine == null) { completionProposals.add(new CompletionProposal(suggestionText, offset, 0, suggestionText.length())); } else { // only include if the suggestion matches if (suggestion.isMatch(startOfLine)) { completionProposals.add(new CompletionProposal(suggestionText, offset - startOfLine.length(), startOfLine.length(), suggestionText.length())); } else if (suggestion.isPartialMatch(startOfLine)) { // TODO: Rich wanted to look at this, by doing something // funky with partial patterns or something completionProposals.add(new CompletionProposal(suggestionText, offset - startOfLine.length(), startOfLine.length(), suggestionText.length())); } } existingSuggestions.add(suggestion); } } return completionProposals; } private boolean isExistingSuggestion(final String suggestionText, final Set<Suggestion> existingSuggestions) { for (final Suggestion suggestion : existingSuggestions) { if (suggestion.isMatch(suggestionText)) { return true; } } return false; } /** * Returns the current line, up to the point offset * * @param document * The currently edited document * @param offset * position in the document of the cursor * @return */ private String getLineUpTo(final IDocument document, final int offset) { try { final int lineNumber = document.getLineOfOffset(offset); final int lineStart = document.getLineOffset(lineNumber); final String line = document.get(lineStart, offset - lineStart); return removeLeadingSpace(line); } catch (final BadLocationException e) { FeatureEditorPlugin.instance().error("Could not get last word"); // ... log the exception ... } return null; } private String removeLeadingSpace(final String text) { return text.replaceAll("^\\s+", ""); } }