package com.redhat.ceylon.eclipse.code.complete; import static com.redhat.ceylon.compiler.typechecker.tree.TreeUtil.formatPath; import static com.redhat.ceylon.eclipse.code.complete.ParameterContextValidator.findCharCount; import static com.redhat.ceylon.eclipse.util.Nodes.getReferencedNode; import static com.redhat.ceylon.model.typechecker.model.ModelUtil.isNameMatching; import static java.util.Collections.singletonList; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import org.antlr.runtime.CommonToken; import org.eclipse.jface.text.BadLocationException; 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 com.redhat.ceylon.compiler.typechecker.tree.Node; import com.redhat.ceylon.compiler.typechecker.tree.Tree; import com.redhat.ceylon.compiler.typechecker.tree.Visitor; import com.redhat.ceylon.eclipse.code.parse.CeylonParseController; import com.redhat.ceylon.eclipse.util.Nodes; import com.redhat.ceylon.ide.common.util.escaping_; import com.redhat.ceylon.model.typechecker.model.Class; import com.redhat.ceylon.model.typechecker.model.Constructor; import com.redhat.ceylon.model.typechecker.model.Declaration; import com.redhat.ceylon.model.typechecker.model.DeclarationWithProximity; import com.redhat.ceylon.model.typechecker.model.Function; import com.redhat.ceylon.model.typechecker.model.FunctionOrValue; import com.redhat.ceylon.model.typechecker.model.Functional; import com.redhat.ceylon.model.typechecker.model.Interface; import com.redhat.ceylon.model.typechecker.model.Parameter; import com.redhat.ceylon.model.typechecker.model.ParameterList; import com.redhat.ceylon.model.typechecker.model.Scope; import com.redhat.ceylon.model.typechecker.model.Type; import com.redhat.ceylon.model.typechecker.model.TypeDeclaration; import com.redhat.ceylon.model.typechecker.model.TypeParameter; import com.redhat.ceylon.model.typechecker.model.Unit; import com.redhat.ceylon.model.typechecker.model.Value; public class CompletionUtil { public static List<Declaration> overloads(Declaration dec) { return dec.isAbstraction() ? dec.getOverloads() : singletonList(dec); } static List<Parameter> getParameters(ParameterList pl, boolean includeDefaults, boolean namedInvocation) { List<Parameter> ps = pl.getParameters(); if (includeDefaults) { return ps; } else { List<Parameter> list = new ArrayList<Parameter>(); for (Parameter p: ps) { if (!p.isDefaulted() || (namedInvocation && spreadable(p, ps))) { list.add(p); } } return list; } } private static boolean spreadable(Parameter param, List<Parameter> list) { Parameter lastParam = list.get(list.size()-1); if (param==lastParam && param.getModel() instanceof Value) { Type type = param.getType(); Unit unit = param.getDeclaration().getUnit(); return type!=null && unit.isIterableParameterType(type); } else { return false; } } static String fullPath(int offset, String prefix, Tree.ImportPath path) { StringBuilder fullPath = new StringBuilder(); if (path!=null) { String pathString = formatPath(path.getIdentifiers()); fullPath.append(pathString) .append('.'); int len = offset -path.getStartIndex() -prefix.length(); fullPath.setLength(len); } return fullPath.toString(); } static boolean isPackageDescriptor(CeylonParseController cpc) { Tree.CompilationUnit lcu = cpc.getLastCompilationUnit(); return lcu != null && lcu.getUnit() != null && lcu.getUnit() .getFilename() .equals("package.ceylon"); } static boolean isModuleDescriptor(CeylonParseController cpc) { Tree.CompilationUnit lcu = cpc.getLastCompilationUnit(); return lcu != null && lcu.getUnit() != null && lcu.getUnit() .getFilename() .equals("module.ceylon"); } static boolean isEmptyModuleDescriptor(CeylonParseController cpc) { Tree.CompilationUnit lcu = cpc.getLastCompilationUnit(); return isModuleDescriptor(cpc) && lcu != null && lcu.getModuleDescriptors() .isEmpty(); } static boolean isEmptyPackageDescriptor(CeylonParseController cpc) { Tree.CompilationUnit lcu = cpc.getLastCompilationUnit(); return lcu != null && lcu.getUnit() != null && lcu.getUnit() .getFilename() .equals("package.ceylon") && lcu.getPackageDescriptors() .isEmpty(); } static int nextTokenType(CeylonParseController cpc, CommonToken token) { for (int i=token.getTokenIndex()+1; i<cpc.getTokens().size(); i++) { CommonToken tok = cpc.getTokens().get(i); if (tok.getChannel()!=CommonToken.HIDDEN_CHANNEL) { return tok.getType(); } } return -1; } static int getLine(final int offset, ITextViewer viewer) { int line=-1; try { line = viewer.getDocument().getLineOfOffset(offset); } catch (BadLocationException e) { e.printStackTrace(); } return line; } public static boolean isInBounds(List<Type> upperBounds, Type t) { boolean ok = true; for (Type ub: upperBounds) { if (!t.isSubtypeOf(ub) && !(ub.involvesTypeParameters() && t.getDeclaration() .inherits(ub.getDeclaration()))) { ok = false; break; } } return ok; } public static List<DeclarationWithProximity> getSortedProposedValues(Scope scope, Unit unit) { return getSortedProposedValues(scope, unit, null); } public static List<DeclarationWithProximity> getSortedProposedValues(Scope scope, Unit unit, final String exactName) { Map<String, DeclarationWithProximity> map = scope.getMatchingDeclarations(unit, "", 0, null); if (exactName!=null) { for (DeclarationWithProximity dwp: new ArrayList<DeclarationWithProximity> (map.values())) { if (!dwp.isUnimported() && !dwp.isAlias() && isNameMatching(dwp.getName(), exactName)) { map.put(dwp.getName(), new DeclarationWithProximity( dwp.getDeclaration(), -5)); } } } List<DeclarationWithProximity> results = new ArrayList<DeclarationWithProximity>( map.values()); Collections.sort(results, new ArgumentProposalComparator(exactName)); return results; } public static boolean isIgnoredLanguageModuleClass(Class clazz) { return clazz.isString() || clazz.isInteger() || clazz.isFloat() || clazz.isCharacter() || clazz.isAnnotation(); } public static boolean isIgnoredLanguageModuleValue(Value value) { String name = value.getName(); return name.equals("process") || name.equals("runtime") || name.equals("system") || name.equals("operatingSystem") || name.equals("language") || name.equals("emptyIterator") || name.equals("infinity") || name.endsWith("IntegerValue") || name.equals("finished"); } public static boolean isIgnoredLanguageModuleMethod(Function method) { String name = method.getName(); return name.equals("className") || name.equals("flatten") || name.equals("unflatten")|| name.equals("curry") || name.equals("uncurry") || name.equals("compose") || method.isAnnotation(); } static boolean isIgnoredLanguageModuleType(TypeDeclaration td) { return !td.isObject() && !td.isAnything() && !td.isString() && !td.isInteger() && !td.isCharacter() && !td.isFloat() && !td.isBoolean(); } public static String getInitialValueDescription( final Declaration dec, CeylonParseController cpc) { if (cpc!=null) { Node refnode = getReferencedNode(dec); Tree.SpecifierOrInitializerExpression sie = null; String arrow = null; if (refnode instanceof Tree.AttributeDeclaration) { Tree.AttributeDeclaration ad = (Tree.AttributeDeclaration) refnode; sie = ad.getSpecifierOrInitializerExpression(); arrow = " = "; } else if (refnode instanceof Tree.MethodDeclaration) { Tree.MethodDeclaration md = (Tree.MethodDeclaration) refnode; sie = md.getSpecifierExpression(); arrow = " => "; } Tree.CompilationUnit lcu = cpc.getLastCompilationUnit(); if (sie==null) { class FindInitializerVisitor extends Visitor { Tree.SpecifierOrInitializerExpression result; @Override public void visit( Tree.InitializerParameter that) { super.visit(that); Declaration d = that.getParameterModel() .getModel(); if (d!=null && d.equals(dec)) { result = that.getSpecifierExpression(); } } } FindInitializerVisitor fiv = new FindInitializerVisitor(); fiv.visit(lcu); sie = fiv.result; } if (sie!=null) { Tree.Expression e = sie.getExpression(); if (e!=null) { Tree.Term term = e.getTerm(); if (term instanceof Tree.Literal) { String text = term.getToken() .getText(); if (text.length()<20) { return arrow + text; } } else if (term instanceof Tree.BaseMemberOrTypeExpression) { Tree.BaseMemberOrTypeExpression bme = (Tree.BaseMemberOrTypeExpression) term; Tree.Identifier id = bme.getIdentifier(); if (id!=null && bme.getTypeArguments()==null) { return arrow + id.getText(); } } else if (term!=null) { Unit unit = lcu.getUnit(); if (term.getUnit().equals(unit)) { String impl = Nodes.text(term, cpc.getTokens()); if (impl.length()<10) { return arrow + impl; } } } //don't have the token stream :-/ //TODO: figure out where to get it from! return arrow + "..."; } } } return ""; } public static String getDefaultValueDescription( Parameter param, CeylonParseController cpc) { if (param.isDefaulted()) { FunctionOrValue model = param.getModel(); if (model instanceof Functional) { return " => ..."; } else { return getInitialValueDescription(model, cpc); } } else { return ""; } } static String anonFunctionHeader( Type requiredType, Unit unit) { StringBuilder text = new StringBuilder(); text.append("("); boolean first = true; char c = 'a'; List<Type> argTypes = unit.getCallableArgumentTypes(requiredType); for (Type paramType: argTypes) { if (first) { first = false; } else { text.append(", "); } text.append(paramType.asSourceCodeString(unit)) .append(" ") .append(c++); } text.append(")"); return text.toString(); } public static IRegion getCurrentSpecifierRegion( IDocument document, int offset) throws BadLocationException { int start = offset; int length = 0; for (int i=offset; i<document.getLength(); i++) { char ch = document.getChar(i); if (Character.isWhitespace(ch) || ch==';'||ch==','||ch==')') { break; } length++; } return new Region(start, length); } public static String getProposedName( Declaration qualifier, Declaration dec, Unit unit) { StringBuilder buf = new StringBuilder(); if (qualifier!=null) { buf.append(escaping_.get_().escapeName(qualifier, unit)) .append('.'); } if (dec instanceof Constructor) { Constructor constructor = (Constructor) dec; TypeDeclaration clazz = constructor.getExtendedType() .getDeclaration(); buf.append(escaping_.get_().escapeName(clazz, unit)) .append('.'); } buf.append(escaping_.get_().escapeName(dec, unit)); return buf.toString(); } static IRegion getCurrentArgumentRegion( IDocument document, int loc, int index, int startOfArgs) throws BadLocationException { IRegion li = document.getLineInformationOfOffset(loc); int endOfLine = li.getOffset() + li.getLength(); int offset = findCharCount(index, document, loc+startOfArgs, endOfLine, ",;", "", true) + 1; if (offset>0 && document.getChar(offset)==' ') { offset++; } int nextOffset = findCharCount(index+1, document, loc+startOfArgs, endOfLine, ",;", "", true); int middleOffset = findCharCount(1, document, offset, nextOffset, "=", "", true)+1; if (middleOffset>0 && document.getChar(middleOffset)=='>') { middleOffset++; } while (middleOffset>0 && document.getChar(middleOffset)==' ') { middleOffset++; } if (middleOffset>offset && middleOffset<nextOffset) { offset = middleOffset; } if (nextOffset==-1) { nextOffset = offset; } return new Region(offset, nextOffset-offset); } public static String[] getAssignableLiterals( Type type, Unit unit) { TypeDeclaration dtd = unit.getDefiniteType(type) .getDeclaration(); if (dtd instanceof Class) { if (dtd.isInteger()) { return new String[] { "0", "1", "2" }; } if (dtd.isByte()) { return new String[] { "0.byte", "1.byte" }; } else if (dtd.isFloat()) { return new String[] { "0.0", "1.0", "2.0" }; } else if (dtd.isString()) { return new String[] { "\"\"", "\"\"\"\"\"\"" }; } else if (dtd.isCharacter()) { return new String[] { "' '", "'\\n'", "'\\t'" }; } else { return new String[0]; } } else if (dtd instanceof Interface) { if (dtd.isIterable()) { return new String[] { "{}" }; } else if (dtd.isSequential() || dtd.isEmpty()) { return new String[] { "[]" }; } else { return new String[0]; } } else { return new String[0]; } } @Deprecated protected static boolean withinBounds(Type requiredType, Type type, Scope scope) { TypeDeclaration td = requiredType.resolveAliases().getDeclaration(); if (type.isSubtypeOf(requiredType)) { return true; } else if (td instanceof TypeParameter) { return !td.isDefinedInScope(scope) && isInBounds(td.getSatisfiedTypes(), type); } else if (type.getDeclaration().inherits(td)) { Type supertype = type.getSupertype(td); for (TypeParameter tp: td.getTypeParameters()) { Type ta = supertype.getTypeArguments().get(tp); Type rta = requiredType.getTypeArguments().get(tp); if (ta!=null && rta!=null) { if (!withinBounds(rta, ta, scope)) { return false; } } else { return false; } } return true; } else { return false; } } }