package com.redhat.ceylon.eclipse.code.correct; import static com.redhat.ceylon.eclipse.code.outline.CeylonLabelProvider.getDecoratedImage; import static com.redhat.ceylon.eclipse.code.outline.CeylonLabelProvider.getImageForDeclaration; import static com.redhat.ceylon.eclipse.ui.CeylonResources.CEYLON_LITERAL; import static com.redhat.ceylon.model.typechecker.model.ModelUtil.isTypeUnknown; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashSet; import java.util.List; import java.util.Set; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.DocumentEvent; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.IRegion; import org.eclipse.jface.text.ITextViewer; import org.eclipse.jface.text.Region; import org.eclipse.jface.text.contentassist.ICompletionProposal; import org.eclipse.jface.text.contentassist.ICompletionProposalExtension2; import org.eclipse.jface.text.contentassist.ICompletionProposalExtension6; import org.eclipse.jface.text.contentassist.IContextInformation; import org.eclipse.jface.viewers.StyledString; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.Point; import com.redhat.ceylon.eclipse.ui.CeylonPlugin; import com.redhat.ceylon.eclipse.util.Highlights; import com.redhat.ceylon.model.typechecker.model.ModelUtil; import com.redhat.ceylon.model.typechecker.model.Type; import com.redhat.ceylon.model.typechecker.model.TypeDeclaration; import com.redhat.ceylon.model.typechecker.model.Unit; public class LinkedModeCompletionProposal implements ICompletionProposal, ICompletionProposalExtension2, ICompletionProposalExtension6 { static final class NullProposal implements ICompletionProposal, ICompletionProposalExtension2 { private List<ICompletionProposal> proposals; NullProposal(List<ICompletionProposal> proposals) { this.proposals = proposals; } @Override public void apply(IDocument document) {} @Override public Point getSelection(IDocument document) { return null; } @Override public String getAdditionalProposalInfo() { return null; } @Override public String getDisplayString() { return ""; } @Override public Image getImage() { return null; } @Override public IContextInformation getContextInformation() { return null; } @Override public void apply(ITextViewer viewer, char trigger, int stateMask, int offset) {} @Override public void selected(ITextViewer viewer, boolean smartToggle) {} @Override public void unselected(ITextViewer viewer) {} @Override public boolean validate(IDocument document, int offset, DocumentEvent event) { for (ICompletionProposal p: proposals) { if (p instanceof ICompletionProposalExtension2) { ICompletionProposalExtension2 ext = (ICompletionProposalExtension2) p; if (ext.validate(document, offset, event)) { return true; } } else { return true; } } return false; } } private final String text; private final Image image; private final int offset; private int position; private String description; private String breakChars; private String regionChars; LinkedModeCompletionProposal(String name, int offset, int position) { this(name, offset, name, position, null); breakChars = "(<"; regionChars = "<>[](){}*+.,|&?\\"; } LinkedModeCompletionProposal(Type type, Unit unit, int offset, int position) { this(type.asString(unit), offset, type.asSourceCodeString(unit), position, getImageForDeclaration(type.getDeclaration())); } LinkedModeCompletionProposal(String description, int offset, String text, int position, Image image) { this.description = description; this.text=text; this.position = position; this.image = image; this.offset = offset; regionChars = "<>[](){}*+.,|&?\\"; breakChars = ""; } @Override public Image getImage() { return image; } @Override public Point getSelection(IDocument document) { return new Point(offset + text.length(), 0); } public void apply(IDocument document) { try { IRegion region = getCurrentRegion(document); document.replace(region.getOffset(), region.getLength(), text); } catch (BadLocationException e) { e.printStackTrace(); } } protected IRegion getCurrentRegion(IDocument document) throws BadLocationException { int start = offset; int length = 0; int count = 0; boolean lastWasWs = false; int depth = 0; for (int i=offset; i<document.getLength(); i++) { char ch = document.getChar(i); if (Character.isWhitespace(ch) || count==position && breakChars.indexOf(ch)>=0) { if (!lastWasWs) { if (count++==position) { break; } lastWasWs = true; } start = i+1; length = 0; } else if (Character.isJavaIdentifierPart(ch) || regionChars.indexOf(ch)>=0 || depth > 0) { lastWasWs = false; length++; if (ch=='('||ch=='<'||ch=='['||ch=='{') depth++; if (ch==')'||ch=='>'||ch==']'||ch=='}') depth--; } } return new Region(start, length); } public String getDisplayString() { return description; } @Override public StyledString getStyledDisplayString() { StyledString result = new StyledString(); Highlights.styleFragment(result, getDisplayString(), false, null, CeylonPlugin.getCompletionFont()); return result; } public String getAdditionalProposalInfo() { return null; } @Override public IContextInformation getContextInformation() { return null; } @Override public void apply(ITextViewer viewer, char trigger, int stateMask, int 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) { try { IRegion region = getCurrentRegion(document); String prefix = document.get(region.getOffset(), offset-region.getOffset()); String filter = prefix.trim().toLowerCase(); return ModelUtil.isNameMatching(prefix, text) || text.toLowerCase().startsWith(filter); } catch (BadLocationException e) { return false; } } public static ICompletionProposal[] getNameProposals(int offset, int seq, String[] names) { return getNameProposals(offset, seq, names, null); } public static ICompletionProposal[] getNameProposals(int offset, int seq, String[] names, String defaultName) { List<ICompletionProposal> nameProposals = new ArrayList<ICompletionProposal>(); Set<String> proposedNames = new HashSet<String>(); if (defaultName!=null) { LinkedModeCompletionProposal nameProposal = new LinkedModeCompletionProposal(defaultName, offset, seq); nameProposals.add(nameProposal); proposedNames.add(defaultName); } for (String name: names) { if (proposedNames.add(name)) { if (defaultName==null || !defaultName.equals(name)) { LinkedModeCompletionProposal nameProposal = new LinkedModeCompletionProposal(name, offset, seq); nameProposals.add(nameProposal); } } } ICompletionProposal[] proposals = new ICompletionProposal[nameProposals.size() + 1]; int i=0; proposals[i++] = new NullProposal(nameProposals); for (ICompletionProposal tp: nameProposals) { proposals[i++] = tp; } return proposals; } // Ceylon version in AssignToLocalProposal.getSupertypes() public static ICompletionProposal[] getSupertypeProposals(int offset, Unit unit, final Type type, boolean includeValue, String kind, final LinkedModeImporter importer) { if (type==null) { return new ICompletionProposal[0]; } TypeDeclaration td = type.getDeclaration(); List<TypeDeclaration> supertypes = isTypeUnknown(type) || type.isTypeConstructor() ? Collections.<TypeDeclaration>emptyList() : td.getSupertypeDeclarations(); int size = supertypes.size(); if (includeValue) size++; if (type.isTypeConstructor() || type.isUnion() || type.isIntersection()) { size++; } ICompletionProposal[] typeProposals = new ICompletionProposal[size]; int i=0; if (includeValue) { typeProposals[i++] = new LinkedModeCompletionProposal(kind, offset, kind, 0, getDecoratedImage(CEYLON_LITERAL, 0, false)) { @Override public void apply(IDocument document) { super.apply(document); importer.selected(null); } }; } if (type.isTypeConstructor() || type.isUnion() || type.isIntersection()) { typeProposals[i++] = new LinkedModeCompletionProposal(type, unit, offset, 0) { @Override public void apply(IDocument document) { super.apply(document); importer.selected(type); } }; } Collections.sort(supertypes, new Comparator<TypeDeclaration>() { @Override public int compare(TypeDeclaration x, TypeDeclaration y) { if (x.inherits(y)) { return 1; } if (y.inherits(x)) { return -1; } return y.getName().compareTo(x.getName()); } }); for (int j=supertypes.size()-1; j>=0; j--) { final Type supertype = type.getSupertype(supertypes.get(j)); typeProposals[i++] = new LinkedModeCompletionProposal(supertype, unit, offset, 0) { @Override public void apply(IDocument document) { super.apply(document); importer.selected(supertype); } }; } return typeProposals; } public static ICompletionProposal[] getCaseTypeProposals(int offset, Unit unit, Type type) { if (type==null) { return new ICompletionProposal[0]; } List<Type> caseTypes = type.getCaseTypes(); if (caseTypes==null) { return new ICompletionProposal[0]; } ICompletionProposal[] typeProposals = new ICompletionProposal[caseTypes.size()]; for (int i=0; i<caseTypes.size(); i++) { Type ct = caseTypes.get(i); typeProposals[i] = new LinkedModeCompletionProposal(ct, unit, offset, 0); } return typeProposals; } }