package com.redhat.ceylon.eclipse.code.correct; import static com.redhat.ceylon.eclipse.ui.CeylonResources.MINOR_CHANGE; import static com.redhat.ceylon.eclipse.util.Nodes.findStatement; import java.util.Collection; import java.util.List; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.contentassist.ICompletionProposal; import org.eclipse.jface.text.contentassist.ICompletionProposalExtension6; import org.eclipse.jface.text.contentassist.IContextInformation; import org.eclipse.jface.viewers.StyledString; import org.eclipse.ltk.core.refactoring.DocumentChange; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.Point; import org.eclipse.text.edits.InsertEdit; import org.eclipse.text.edits.MultiTextEdit; import com.redhat.ceylon.compiler.typechecker.tree.Node; import com.redhat.ceylon.compiler.typechecker.tree.Tree; import com.redhat.ceylon.compiler.typechecker.tree.Tree.Annotation; import com.redhat.ceylon.compiler.typechecker.tree.Tree.Identifier; import com.redhat.ceylon.compiler.typechecker.tree.Tree.Primary; import com.redhat.ceylon.compiler.typechecker.tree.Tree.TypedDeclaration; import com.redhat.ceylon.eclipse.util.EditorUtil; import com.redhat.ceylon.model.typechecker.model.Declaration; class PrintProposal implements ICompletionProposal, ICompletionProposalExtension6 { private final Node node; private final Tree.CompilationUnit rootNode; private final int currentOffset; public PrintProposal(Tree.CompilationUnit cu, Node node, int currentOffset) { this.rootNode = cu; this.node = node; this.currentOffset = currentOffset; } @Override public void apply(IDocument document) { Tree.Statement st = findStatement(rootNode, node); Node expression; Node expanse; if (st instanceof Tree.ExpressionStatement) { Tree.ExpressionStatement es = (Tree.ExpressionStatement) st; Tree.Expression e = es.getExpression(); expression = e; expanse = st; Tree.Term term = e.getTerm(); if (term instanceof Tree.InvocationExpression) { Tree.InvocationExpression ie = (Tree.InvocationExpression) term; Primary primary = ie.getPrimary(); if (primary instanceof Tree.QualifiedMemberExpression) { Tree.QualifiedMemberExpression prim = (Tree.QualifiedMemberExpression) primary; if (prim.getMemberOperator().getToken()==null) { //an expression followed by two annotations //can look like a named operator expression //even though that is disallowed as an //expression statement Tree.Primary p = prim.getPrimary(); expression = p; expanse = expression; } } } } else if (st instanceof Tree.Declaration) { Tree.Declaration dec = (Tree.Declaration) st; Declaration d = dec.getDeclarationModel(); if (d==null || d.isToplevel()) { return; } //some expressions get interpreted as annotations Tree.AnnotationList al = dec.getAnnotationList(); List<Tree.Annotation> annotations = al.getAnnotations(); Tree.AnonymousAnnotation aa = al.getAnonymousAnnotation(); if (aa!=null && currentOffset<=aa.getEndIndex()) { expression = aa; expanse = expression; } else if (!annotations.isEmpty() && currentOffset<=al.getEndIndex()) { Tree.Annotation a = annotations.get(0); expression = a; expanse = expression; } else if (st instanceof Tree.TypedDeclaration) { //some expressions look like a type declaration //when they appear right in front of an annotation //or function invocations Tree.TypedDeclaration td = (Tree.TypedDeclaration) st; Tree.Type type = td.getType(); if (type instanceof Tree.SimpleType || type instanceof Tree.FunctionType) { expression = type; expanse = expression; } else { return; } } else { return; } } else { return; } // int stopIndex = expanse.getEndIndex()-1; // if (currentOffset<expanse.getStartIndex() || // currentOffset>stopIndex+1) { // return; // } int offset = expanse.getStartIndex(); DocumentChange change = new DocumentChange("Print Expression", document); change.setEdit(new MultiTextEdit()); change.addEdit(new InsertEdit(offset, "print(")); String terminal = expanse.getEndToken().getText(); String close = ")"; if (!terminal.equals(";")) { stopIndex++; close = ");"; } change.addEdit(new InsertEdit(stopIndex, close)); EditorUtil.performChange(change); } @Override public Point getSelection(IDocument document) { return new Point(currentOffset+6,0); } @Override public String getAdditionalProposalInfo() { return null; } @Override public String getDisplayString() { return "Print expression"; } @Override public StyledString getStyledDisplayString() { String hint = CorrectionUtil.shortcut( "com.redhat.ceylon.eclipse.ui.action.print"); return new StyledString(getDisplayString()) .append(hint, StyledString.QUALIFIER_STYLER); } @Override public Image getImage() { return MINOR_CHANGE; } @Override public IContextInformation getContextInformation() { return null; } static void addPrintProposal(Tree.CompilationUnit cu, Collection<ICompletionProposal> proposals, Node node, int currentOffset) { PrintProposal prop = new PrintProposal(cu, node, currentOffset); if (prop.isEnabled()) { proposals.add(prop); } } private boolean isEnabled() { Tree.Statement st = findStatement(rootNode, node); if (st instanceof Tree.ExpressionStatement) { return true; } else if (st instanceof Tree.Declaration) { Tree.Declaration dec = (Tree.Declaration) st; Identifier id = dec.getIdentifier(); if (id==null) { return false; } int line = id.getToken().getLine(); Declaration d = dec.getDeclarationModel(); if (d==null || d.isToplevel()) { return false; } //some expressions get interpreted as annotations Tree.AnnotationList al = dec.getAnnotationList(); List<Annotation> annotations = al.getAnnotations(); Tree.AnonymousAnnotation aa = al.getAnonymousAnnotation(); if (aa!=null && currentOffset<=aa.getEndIndex()) { return aa.getEndToken().getLine()!=line; } else if (!annotations.isEmpty() && currentOffset<=al.getEndIndex()) { return al.getEndToken().getLine()!=line; } else if (st instanceof Tree.TypedDeclaration && !(st instanceof Tree.ObjectDefinition)) { //some expressions look like a type declaration //when they appear right in front of an annotation //or function invocations TypedDeclaration td = (Tree.TypedDeclaration) st; Tree.Type type = td.getType(); if (currentOffset<=type.getEndIndex()) { return (type instanceof Tree.SimpleType || type instanceof Tree.FunctionType) && currentOffset<=type.getEndIndex() && currentOffset>=type.getStartIndex() && type.getEndToken().getLine()!=line; } } } return false; } }