package com.redhat.ceylon.eclipse.code.complete; import static com.redhat.ceylon.eclipse.code.outline.CeylonLabelProvider.getImageForDeclaration; import static com.redhat.ceylon.eclipse.code.preferences.CeylonPreferenceInitializer.COMPLETION; import static com.redhat.ceylon.model.typechecker.model.ModelUtil.isNameMatching; import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.DocumentEvent; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.IInformationControlCreator; import org.eclipse.jface.text.ITextViewer; import org.eclipse.jface.text.contentassist.ICompletionProposal; import org.eclipse.jface.text.contentassist.ICompletionProposalExtension2; import org.eclipse.jface.text.contentassist.ICompletionProposalExtension3; import org.eclipse.jface.text.contentassist.ICompletionProposalExtension4; import org.eclipse.jface.text.contentassist.ICompletionProposalExtension5; import org.eclipse.jface.text.contentassist.ICompletionProposalExtension6; import org.eclipse.jface.text.contentassist.IContextInformation; import org.eclipse.jface.viewers.StyledString; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.Point; import org.eclipse.text.edits.ReplaceEdit; import com.redhat.ceylon.eclipse.ui.CeylonPlugin; import com.redhat.ceylon.eclipse.util.Highlights; import com.redhat.ceylon.ide.common.doc.Icons; import com.redhat.ceylon.model.typechecker.model.Declaration; public class CompletionProposal implements ICompletionProposal, ICompletionProposalExtension2, ICompletionProposalExtension4, ICompletionProposalExtension6, ICompletionProposalExtension3, ICompletionProposalExtension5 { protected final String text; private final ImageRetriever imageRetriever; protected final String prefix; private final String description; protected int offset; private int length; private boolean toggleOverwrite; protected String currentPrefix; public static interface ImageRetriever { Image getImage(); } public static class DeclarationImageRetriever implements ImageRetriever { private Declaration decl; public DeclarationImageRetriever(Declaration declaration) { decl = declaration; } @Override public Image getImage() { return getImageForDeclaration(decl); } } public static class IconImageRetriever implements ImageRetriever { private Icons icon; public IconImageRetriever(Icons icon) { this.icon = icon; } @Override public Image getImage() { return com.redhat.ceylon.eclipse.util.eclipseIcons_.get_().fromIcons(icon); } } public static class FixedImageRetriever implements ImageRetriever { private Image image; public FixedImageRetriever(Image image) { this.image = image; } @Override public Image getImage() { return image; } } public CompletionProposal(int offset, String prefix, ImageRetriever imageRetriever, String desc, String text) { this.text = text; this.imageRetriever = imageRetriever; this.offset = offset; this.prefix = prefix; currentPrefix = prefix; this.length = prefix.length(); this.description = desc; Assert.isNotNull(description); } @Override public Image getImage() { return imageRetriever.getImage(); } @Override public Point getSelection(IDocument document) { return new Point(offset + text.length() - prefix.length(), 0); } public void apply(IDocument document) { try { document.replace(start(), length(document), withoutDupeSemi(document)); } catch (BadLocationException e) { e.printStackTrace(); } } protected ReplaceEdit createEdit(IDocument document) { return new ReplaceEdit(start(), length(document), withoutDupeSemi(document)); } public int length(IDocument document) { String overwrite = CeylonPlugin.getPreferences().getString(COMPLETION); if ("overwrite".equals(overwrite)!=toggleOverwrite) { int length = prefix.length(); try { for (int i=offset; i<document.getLength() && Character.isJavaIdentifierPart(document.getChar(i)); i++) { length++; } } catch (BadLocationException e) { e.printStackTrace(); } return length; } else { return this.length; } } public int start() { return offset-prefix.length(); } public String withoutDupeSemi(IDocument document) { try { if (text.endsWith(";") && document.getChar(offset)==';') { return text.substring(0,text.length()-1); } } catch (BadLocationException e) { e.printStackTrace(); } return text; } public String getDisplayString() { return description; } @Override public String getAdditionalProposalInfo() { return null; } @Override public Object getAdditionalProposalInfo(IProgressMonitor monitor) { return null; } @Override public boolean isAutoInsertable() { return true; } protected boolean qualifiedNameIsPath() { return false; } @Override public StyledString getStyledDisplayString() { StyledString result = new StyledString(); Highlights.styleFragment(result, getDisplayString(), qualifiedNameIsPath(), currentPrefix, CeylonPlugin.getCompletionFont()); return result; } @Override public IContextInformation getContextInformation() { return null; } @Override public void apply(ITextViewer viewer, char trigger, int stateMask, int offset) { toggleOverwrite = (stateMask&SWT.CTRL)!=0; length = prefix.length() + offset - this.offset; apply(viewer.getDocument()); } @Override public void selected(ITextViewer viewer, boolean smartToggle) {} @Override public void unselected(ITextViewer viewer) {} @Override public boolean validate(IDocument document, int offset, DocumentEvent event) { if (offset<this.offset) { return false; } currentPrefix = getCurrentPrefix(document, offset); return currentPrefix==null ? false : isProposalMatching(currentPrefix, text); } /** * To be overridden by subclasses */ protected boolean isProposalMatching(String currentPrefix, String text){ return isNameMatching(currentPrefix, text); } String getCurrentPrefix(IDocument document, int offset) { try { int start = this.offset-prefix.length(); return document.get(start, offset-start); } catch (BadLocationException e) { return null; } } @Override public IInformationControlCreator getInformationControlCreator() { return null; } @Override public CharSequence getPrefixCompletionText(IDocument document, int completionOffset) { return withoutDupeSemi(document); } @Override public int getPrefixCompletionStart(IDocument document, int completionOffset) { return start(); } }