/**
* This file Copyright (c) 2005-2008 Aptana, Inc. This program is
* dual-licensed under both the Aptana Public License and the GNU General
* Public license. You may elect to use one or the other of these licenses.
*
* This program is distributed in the hope that it will be useful, but
* AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
* NONINFRINGEMENT. Redistribution, except as permitted by whichever of
* the GPL or APL youelect, is prohibited.
*
* 1. For the GPL license (GPL), you can redistribute and/or modify this
* program under the terms of the GNU General Public License,
* Version 3, as published by the Free Software Foundation. You should
* have received a copy of the GNU General Public License, Version 3 along
* with this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Aptana provides a special exception to allow redistribution of this file
* with certain other free and open source software ("FOSS") code and certain additional terms
* pursuant to Section 7 of the GPL. You may view the exception and these
* terms on the web at http://www.aptana.com/legal/gpl/.
*
* 2. For the Aptana Public License (APL), this program and the
* accompanying materials are made available under the terms of the APL
* v1.0 which accompanies this distribution, and is available at
* http://www.aptana.com/legal/apl/.
*
* You may view the GPL, Aptana's exception and additional terms, and the
* APL in the file titled license.html at the root of the corresponding
* plugin containing this source file.
*
* Any modifications to this file must keep this entire header intact.
*/
package com.aptana.ide.editor.erb.contentassist;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Vector;
import java.util.regex.Pattern;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.jface.text.TextPresentation;
import org.eclipse.jface.text.contentassist.ContextInformation;
import org.eclipse.jface.text.contentassist.ICompletionProposal;
import org.eclipse.jface.text.contentassist.IContextInformation;
import org.eclipse.jface.text.contentassist.IContextInformationPresenter;
import org.eclipse.jface.text.contentassist.IContextInformationValidator;
import org.eclipse.jface.text.templates.Template;
import org.eclipse.jface.text.templates.TemplateCompletionProcessor;
import org.eclipse.jface.text.templates.TemplateContextType;
import org.eclipse.swt.graphics.Image;
import org.rubypeople.rdt.internal.ui.rubyeditor.RubyEditorMessages;
import com.aptana.ide.core.IdeLog;
import com.aptana.ide.editor.erb.ERBPlugin;
import com.aptana.ide.editors.unified.contentassist.ICompletionProposalContributor;
import com.aptana.ide.lexer.Lexeme;
import com.aptana.ide.lexer.LexemeList;
/**
* @author
*/
public class ERBTemplateCompletionProposalContributor extends TemplateCompletionProcessor implements
ICompletionProposalContributor
{
/**
* @see com.aptana.ide.editors.unified.contentassist.ICompletionProposalContributor#computeCompletionProposals(org.eclipse.jface.text.ITextViewer,
* int, int, com.aptana.ide.lexer.LexemeList, char, char)
*/
public ICompletionProposal[] computeCompletionProposals(ITextViewer viewer, int offset, int position,
LexemeList lexemeList, char activationChar, char previousChar)
{
return this.computeCompletionProposals(viewer, offset, position, lexemeList, activationChar, previousChar,
false);
}
/**
* @see com.aptana.ide.editors.unified.contentassist.ICompletionProposalContributor#computeCompletionProposals(org.eclipse.jface.text.ITextViewer,
* int, int, com.aptana.ide.lexer.LexemeList, char, char, boolean)
*/
public ICompletionProposal[] computeCompletionProposals(ITextViewer viewer, int offset, int position,
LexemeList lexemeList, char activationChar, char previousChar, boolean autoActivated)
{
// TODO Push up code so we never get here. In the HTML processor, it should check the auto activation characters
// of each child if we're auto activated, and only ask those with a match to the activation char
if (autoActivated)
return new ICompletionProposal[0];
Lexeme lex = lexemeList.getLexemeFromOffset(offset);
if (lex != null && lex.getText().equals("\"\""))
return new ICompletionProposal[0];
return this.computeCompletionProposals(viewer, offset);
}
private static final Image templateImage = ERBPlugin.imageDescriptorFromPlugin("com.aptana.ide.editor.erb",
"icons/rails.gif").createImage();
private static final char[] COMPLETION_CHARS = { '.' };
/**
* proposalList
*/
protected Vector proposalList = new Vector();
/**
* validator
*/
protected IContextInformationValidator validator = new Validator();
private ITextViewer viewer;
/**
* Simple content assist tip closer. The tip is valid in a range of 5 characters around its popup location.
*/
protected static class Validator implements IContextInformationValidator, IContextInformationPresenter
{
/**
* fInstallOffset
*/
protected int fInstallOffset;
/**
* @see IContextInformationValidator#isContextInformationValid(int)
*/
public boolean isContextInformationValid(int offset)
{
return Math.abs(fInstallOffset - offset) < 5;
}
/**
* @see IContextInformationValidator#install(IContextInformation, ITextViewer, int)
*/
public void install(IContextInformation info, ITextViewer viewer, int offset)
{
fInstallOffset = offset;
}
/**
* @see org.eclipse.jface.text.contentassist.IContextInformationPresenter#updatePresentation(int,
* TextPresentation)
*/
public boolean updatePresentation(int documentPosition, TextPresentation presentation)
{
return false;
}
}
/**
* @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#computeCompletionProposals(org.eclipse.jface.text.ITextViewer,
* int)
*/
public ICompletionProposal[] computeCompletionProposals(ITextViewer viewer, int documentOffset)
{
this.viewer = viewer;
WordPartDetector wordPart = new WordPartDetector(viewer, documentOffset);
return turnProposalVectorIntoAdaptedArray(wordPart, viewer, documentOffset);
}
/**
* Turns the vector into an Array of ICompletionProposal objects
*
* @param word
* @param viewer
* @param documentOffset
* @return An array of ICompletionProposals based on the list.
*/
protected ICompletionProposal[] turnProposalVectorIntoAdaptedArray(WordPartDetector word, ITextViewer viewer,
int documentOffset)
{
ICompletionProposal[] templates = determineTemplateProposals(viewer, documentOffset);
return templates;
}
/**
* @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#computeContextInformation(org.eclipse.jface.text.ITextViewer,
* int)
*/
public IContextInformation[] computeContextInformation(ITextViewer viewer, int offset)
{
IContextInformation[] result = new IContextInformation[5];
for (int i = 0; i < result.length; i++)
result[i] = new ContextInformation(MessageFormat.format(RubyEditorMessages.getResourceBundle().getString(
"CompletionProcessor.ContextInfo.display.pattern"), new Object[] { new Integer(i),
new Integer(offset) }), //$NON-NLS-1$
MessageFormat.format(RubyEditorMessages.getResourceBundle().getString(
"CompletionProcessor.ContextInfo.value.pattern"), //$NON-NLS-1$
new Object[] { new Integer(i), new Integer(offset - 5), new Integer(offset + 5) }));
return result;
}
/**
* @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#getCompletionProposalAutoActivationCharacters()
*/
public char[] getCompletionProposalAutoActivationCharacters()
{
return COMPLETION_CHARS;
}
/**
* @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#getContextInformationAutoActivationCharacters()
*/
public char[] getContextInformationAutoActivationCharacters()
{
return null;
}
/**
* @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#getErrorMessage()
*/
public String getErrorMessage()
{
return null;
}
/**
* @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#getContextInformationValidator()
*/
public IContextInformationValidator getContextInformationValidator()
{
return validator;
}
/**
* getPrefix
*
* @param doc
* @param offset
* @return String
*/
protected String getPrefix(IDocument doc, int offset)
{
int length = 0;
String prefix = null;
try
{
while ((offset - length > 0) && Pattern.matches("\\w", doc.get(offset - length - 1, 1)))
{
length++;
}
prefix = doc.get(offset - length, length);
}
catch (BadLocationException e)
{
IdeLog.logError(ERBPlugin.getDefault(), "Error gathering proposal prefix", e);
}
return prefix;
}
/**
* @see org.eclipse.jface.text.templates.TemplateCompletionProcessor#extractPrefix(org.eclipse.jface.text.ITextViewer,
* int)
*/
protected String extractPrefix(ITextViewer viewer, int offset)
{
int i = offset;
IDocument document = viewer.getDocument();
if (i > document.getLength())
{
return ""; //$NON-NLS-1$
}
try
{
while (i > 0)
{
char ch = document.getChar(i - 1);
if (Character.isJavaIdentifierPart(ch))
{
i--;
String pref = null;
if (i - 4 > 0)
{
pref = document.get(i - 4, 3);
if (pref.equals("<%="))
{
i -= 3;
}
else if (pref.endsWith("<%"))
{
pref = document.get(i - 3, 2);
i -= 2;
}
}
else if (i - 3 > 0)
{
pref = document.get(i - 3, 2);
if (pref.endsWith("<%"))
{
i -= 2;
}
}
}
else if (ch == '=')
{
i--;
}
else if (ch == '%')
{
i--;
}
else if (ch == '<')
{
i--;
}
else
{
break;
}
}
return document.get(i, offset - i);
}
catch (BadLocationException e)
{
return ""; //$NON-NLS-1$
}
}
private ICompletionProposal[] determineTemplateProposals(ITextViewer refViewer, int documentOffset)
{
String prefix = getPrefix(viewer.getDocument(), documentOffset);
ICompletionProposal[] matchingTemplateProposals;
if (prefix.length() == 0)
{
matchingTemplateProposals = super.computeCompletionProposals(refViewer, documentOffset);
}
else
{
ICompletionProposal[] templateProposals = super.computeCompletionProposals(refViewer, documentOffset);
List<ICompletionProposal> templateProposalList = new ArrayList<ICompletionProposal>(
templateProposals.length);
for (int i = 0; i < templateProposals.length; i++)
{
if (templateProposals[i].getDisplayString().toLowerCase().startsWith(prefix))
{
templateProposalList.add(templateProposals[i]);
}
}
matchingTemplateProposals = templateProposalList.toArray(new ICompletionProposal[templateProposalList
.size()]);
}
Arrays.sort(matchingTemplateProposals, new TemplateProposalComparator());
return matchingTemplateProposals;
}
/**
* @see org.eclipse.jface.text.templates.TemplateCompletionProcessor#getTemplates(java.lang.String)
*/
protected Template[] getTemplates(String contextTypeId)
{
return RhtmlTemplateManager.getDefault().getTemplateStore().getTemplates();
}
/**
* @see org.eclipse.jface.text.templates.TemplateCompletionProcessor#getContextType(org.eclipse.jface.text.ITextViewer,
* org.eclipse.jface.text.IRegion)
*/
protected TemplateContextType getContextType(ITextViewer viewer, IRegion region)
{
return RhtmlTemplateManager.getDefault().getContextTypeRegistry().getContextType(RhtmlContextType.ID);
}
/**
* @see org.eclipse.jface.text.templates.TemplateCompletionProcessor#getImage(org.eclipse.jface.text.templates.Template)
*/
protected Image getImage(Template template)
{
return templateImage;
}
// ////////////////////////////////////////////////DirAlphaComparator
// To sort directories before files, then alphabetically.
class TemplateProposalComparator implements Comparator<ICompletionProposal>
{
/**
* Comparator interface requires defining compare method.
*
* @param filea
* @param fileb
* @return int
*/
public int compare(ICompletionProposal filea, ICompletionProposal fileb)
{
// ... Sort directories before files,
// otherwise alphabetical ignoring case.
return filea.getDisplayString().compareToIgnoreCase(fileb.getDisplayString());
}
}
}