/** * Aptana Studio * Copyright (c) 2005-2011 by Appcelerator, Inc. All Rights Reserved. * Licensed under the terms of the GNU Public License (GPL) v3 (with exceptions). * Please see the license.html included with this distribution for details. * Any modifications to this file must keep this entire header intact. */ package com.aptana.editor.ruby.internal.contentassist; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.TreeMap; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.ITextViewer; import org.eclipse.jface.text.contentassist.ICompletionProposal; import org.eclipse.jface.text.contentassist.IContextInformation; import com.aptana.editor.common.AbstractThemeableEditor; import com.aptana.editor.common.CommonContentAssistProcessor; import com.aptana.editor.common.contentassist.CommonCompletionProposal; import com.aptana.editor.ruby.RubyEditorPlugin; public class RubyRegexpContentAssistProcessor extends CommonContentAssistProcessor { private static final ICompletionProposal[] NO_PROPOSALS = new ICompletionProposal[0]; private static final IContextInformation[] NO_CONTEXTS = new IContextInformation[0]; /** * Proposal text to description. */ private static final Map<String, String> PROPOSALS = new TreeMap<String, String>(); static { // FIXME Keep ordering of proposals same as here PROPOSALS.put("$", "End of line"); //$NON-NLS-1$ //$NON-NLS-2$ PROPOSALS.put("()", "Grouping"); //$NON-NLS-1$ //$NON-NLS-2$ PROPOSALS.put("*", "Zero or more repetitions of preceding"); //$NON-NLS-1$ //$NON-NLS-2$ PROPOSALS.put("+", "One or more repetitions of preceding"); //$NON-NLS-1$ //$NON-NLS-2$ PROPOSALS.put("?", "At most one repetition of preceding"); //$NON-NLS-1$ //$NON-NLS-2$ PROPOSALS.put("[:alnum:]", "Alphanumeric character class"); //$NON-NLS-1$ //$NON-NLS-2$ PROPOSALS.put("[:alpha:]", "Uppercase or lowercase letter"); //$NON-NLS-1$ //$NON-NLS-2$ PROPOSALS.put("[:blank:]", "Blank and tab"); //$NON-NLS-1$ //$NON-NLS-2$ PROPOSALS.put("[:cntrl:]", "Control characters (at least 0x00-0x1f,0x7f)"); //$NON-NLS-1$ //$NON-NLS-2$ PROPOSALS.put("[:digit:]", "Digit"); //$NON-NLS-1$ //$NON-NLS-2$ PROPOSALS.put("[:graph:]", "Printable character excluding space"); //$NON-NLS-1$ //$NON-NLS-2$ PROPOSALS.put("[:lower:]", "Lowercase letter"); //$NON-NLS-1$ //$NON-NLS-2$ PROPOSALS.put("[:print:]", "Any printable character (including space)"); //$NON-NLS-1$ //$NON-NLS-2$ PROPOSALS.put("[:punct:]", "Printable character excluding space and alp"); //$NON-NLS-1$ //$NON-NLS-2$ PROPOSALS.put("[:space:]", "Whitespace (same as \\s)"); //$NON-NLS-1$ //$NON-NLS-2$ PROPOSALS.put("[:upper:]", "Uppercase letter"); //$NON-NLS-1$ //$NON-NLS-2$ PROPOSALS.put("[:xdigit:]", "Hex digit (0-9, a-f, A-F)"); //$NON-NLS-1$ //$NON-NLS-2$ PROPOSALS.put("\\A", "Beginning of string"); //$NON-NLS-1$ //$NON-NLS-2$ PROPOSALS.put("\\B", "Non-word boundary"); //$NON-NLS-1$ //$NON-NLS-2$ PROPOSALS.put("\\D", "Non-digit character"); //$NON-NLS-1$ //$NON-NLS-2$ PROPOSALS.put("\\S", "Non-space character"); //$NON-NLS-1$ //$NON-NLS-2$ PROPOSALS.put("\\W", "Neither letter or digit"); //$NON-NLS-1$ //$NON-NLS-2$ PROPOSALS.put("\\Z", "End of string (except \\n)"); //$NON-NLS-1$ //$NON-NLS-2$ PROPOSALS.put( "\\b", "Word boundary (outside range specification); Backspace (0x08) (if in a range specification)"); //$NON-NLS-1$ //$NON-NLS-2$ PROPOSALS.put("\\d", "Digit character; same as [0-9]"); //$NON-NLS-1$ //$NON-NLS-2$ PROPOSALS.put("\\s", "Space character; same as [ \\t\\n\\r\\f]"); //$NON-NLS-1$ //$NON-NLS-2$ PROPOSALS.put("\\w", "Letter or digit; same as [0-9A-Za-z]"); //$NON-NLS-1$ //$NON-NLS-2$ PROPOSALS.put("\\z", "End of string"); //$NON-NLS-1$ //$NON-NLS-2$ PROPOSALS.put("^", "Start of line"); //$NON-NLS-1$ //$NON-NLS-2$ PROPOSALS.put("{m, n}", "At least <i>m</i> and at most <i>n</i> repetitions of the preceding"); //$NON-NLS-1$ //$NON-NLS-2$ PROPOSALS.put("|", "Either preceding or next expression may match"); //$NON-NLS-1$ //$NON-NLS-2$ } public RubyRegexpContentAssistProcessor(AbstractThemeableEditor editor) { super(editor); } @Override protected ICompletionProposal[] doComputeCompletionProposals(ITextViewer viewer, int offset, char activationChar, boolean autoActivated) { try { String prefix = getPrefix(viewer, offset); List<ICompletionProposal> result = new ArrayList<ICompletionProposal>(); for (Map.Entry<String, String> entry : PROPOSALS.entrySet()) { if (entry.getKey().startsWith(prefix)) { result.add(createProposal(entry.getKey(), prefix, offset - prefix.length(), entry.getValue())); } } if(result==null || result.size()<1){ return computeWordCompletionProposals(offset, RubyContentAssistProcessor.PROPERTY_IMAGE, null); } return result.toArray(new ICompletionProposal[result.size()]); } catch (BadLocationException x) { // ignore and return no proposals return NO_PROPOSALS; } } private ICompletionProposal createProposal(String proposal, String prefix, int offset, String description) { CommonCompletionProposal p = new CommonCompletionProposal(proposal, offset, prefix.length(), proposal.length(), RubyContentAssistProcessor.PROPERTY_IMAGE, proposal, null, description); return p; } private String getPrefix(ITextViewer viewer, int offset) throws BadLocationException { IDocument doc = viewer.getDocument(); if (doc == null || offset > doc.getLength()) { return null; } int length = 0; while (--offset >= 0 && isPrefixChar(doc.getChar(offset))) { length++; } return doc.get(offset + 1, length); } private boolean isPrefixChar(char c) { switch (c) { case '\\': return true; default: return false; } } public IContextInformation[] computeContextInformation(ITextViewer viewer, int offset) { return NO_CONTEXTS; } protected String getPreferenceNodeQualifier() { return RubyEditorPlugin.PLUGIN_ID; } // public char[] getCompletionProposalAutoActivationCharacters() // { // return new char[] { '\\' }; // } }