package com.redhat.ceylon.eclipse.code.correct;
import static com.redhat.ceylon.eclipse.code.correct.ImportProposals.importProposals;
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.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.DocumentEvent;
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.ICompletionProposalExtension2;
import org.eclipse.jface.text.contentassist.ICompletionProposalExtension6;
import org.eclipse.jface.text.contentassist.IContextInformation;
import org.eclipse.jface.text.link.ProposalPosition;
import org.eclipse.jface.viewers.StyledString;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import com.redhat.ceylon.compiler.typechecker.tree.Tree;
import com.redhat.ceylon.eclipse.platform.platformJ2C;
import com.redhat.ceylon.eclipse.ui.CeylonPlugin;
import com.redhat.ceylon.eclipse.util.Highlights;
import com.redhat.ceylon.ide.common.platform.ReplaceEdit;
import com.redhat.ceylon.ide.common.platform.TextChange;
import com.redhat.ceylon.model.typechecker.model.Declaration;
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;
import ceylon.interop.java.CeylonMutableSet;
public class TypeProposal
implements ICompletionProposal,
ICompletionProposalExtension2,
ICompletionProposalExtension6 {
private final Type type;
private final int offset;
private final String text;
private final Tree.CompilationUnit rootNode;
private Point selection;
private String description;
public TypeProposal(int offset, Type type,
String text, String desc,
Tree.CompilationUnit rootNode) {
this.type = type;
this.offset = offset;
this.description = desc;
this.rootNode = rootNode;
this.text = text;
}
@Override
public void apply(IDocument document) {
TextChange change = new platformJ2C()
.newChange("Specify Type",
new correctJ2C().newDocument(document));
change.initMultiEdit();
HashSet<Declaration> decs =
new HashSet<Declaration>();
if (type!=null) {
importProposals()
.importType(
new CeylonMutableSet<>(null, decs),
type, rootNode);
}
int il = (int) importProposals()
.applyImports(change,
new CeylonMutableSet<>(null, decs),
rootNode, change.getDocument());
change.addEdit(new ReplaceEdit(offset,
getCurrentLength(document), text));
change.apply();
selection = new Point(offset+il, text.length());
}
private int getCurrentLength(IDocument document) {
int length = 0;
for (int i=offset;
i<document.getLength();
i++) {
char ch;
try {
ch = document.getChar(i);
}
catch (BadLocationException e) {
break;
}
if (Character.isWhitespace(ch)) {
break;
}
length++;
}
return length;
}
@Override
public Point getSelection(IDocument document) {
return selection;
}
@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 {
String prefix =
document.get(this.offset,
offset-this.offset);
String filter = prefix.trim().toLowerCase();
return ModelUtil.isNameMatching(prefix, text) ||
text.toLowerCase().startsWith(filter);
}
catch (BadLocationException e) {
return false;
}
}
@Override
public String getAdditionalProposalInfo() {
return null;
}
@Override
public String getDisplayString() {
return description;
}
@Override
public StyledString getStyledDisplayString() {
StyledString result = new StyledString();
Highlights.styleFragment(result,
getDisplayString(), false, null,
CeylonPlugin.getCompletionFont());
return result;
}
@Override
public Image getImage() {
if (type==null) {
return getDecoratedImage(CEYLON_LITERAL, 0, false);
}
else {
return getImageForDeclaration(type.getDeclaration());
}
}
@Override
public IContextInformation getContextInformation() {
return null;
}
public static ProposalPosition getTypeProposals(IDocument document,
int offset, int length, Type infType,
Tree.CompilationUnit rootNode, String kind) {
TypeDeclaration td = infType.getDeclaration();
List<TypeDeclaration> supertypes =
isTypeUnknown(infType) ||
infType.isTypeConstructor() ?
Collections.<TypeDeclaration>emptyList() :
td.getSupertypeDeclarations();
int size = supertypes.size();
if (kind!=null) size++;
if (infType.isTypeConstructor() ||
infType.isTypeParameter() ||
infType.isUnion() ||
infType.isIntersection()) {
size++;
}
ICompletionProposal[] proposals =
new ICompletionProposal[size];
int i=0;
if (kind!=null) {
proposals[i++] =
new TypeProposal(offset, null, kind, kind, rootNode);
}
Unit unit = rootNode.getUnit();
if (infType.isTypeConstructor() ||
infType.isTypeParameter() ||
infType.isUnion() ||
infType.isIntersection()) {
proposals[i++] =
new TypeProposal(offset, infType,
infType.asSourceCodeString(unit),
infType.asString(unit),
rootNode);
}
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--) {
Type type =
infType.getSupertype(supertypes.get(j));
proposals[i++] =
new TypeProposal(offset, type,
type.asSourceCodeString(unit),
type.asString(unit),
rootNode);
}
return new ProposalPosition(document, offset, length,
0, proposals);
}
}