package com.redhat.ceylon.eclipse.code.hover; import com.github.rjeschke.txtmark.SpanEmitter; import com.redhat.ceylon.eclipse.code.html.HTML; import com.redhat.ceylon.model.typechecker.model.Declaration; import com.redhat.ceylon.model.typechecker.model.Function; import com.redhat.ceylon.model.typechecker.model.Module; import com.redhat.ceylon.model.typechecker.model.Package; import com.redhat.ceylon.model.typechecker.model.Referenceable; import com.redhat.ceylon.model.typechecker.model.Scope; import com.redhat.ceylon.model.typechecker.model.Unit; final class CeylonSpanEmitter implements SpanEmitter { private final Scope linkScope; private final Unit unit; CeylonSpanEmitter(Scope linkScope, Unit unit) { this.linkScope = linkScope; this.unit = unit; } @Override public void emitSpan(StringBuilder out, String content) { String linkDescription; String linkTarget; int indexOf = content.indexOf("|"); boolean hasNoPlainText = indexOf == -1; if (hasNoPlainText) { int sep = content.indexOf("::"); linkDescription = sep<0 ? content : content.substring(sep+2); //to match behavior of ceylon doc: /*if (linkDescription.startsWith("package ")) { linkDescription = linkDescription.substring(8); } if (linkDescription.startsWith("module ")) { linkDescription = linkDescription.substring(7); }*/ linkTarget = content; } else { linkDescription = content.substring(0, indexOf); linkTarget = content.substring(indexOf+1, content.length()); } Referenceable decl = resolveLink(linkTarget, linkScope, unit); String href = decl != null ? HTML.link(decl) : null; if (href != null) { out.append("<a ").append(href).append(">"); } if (hasNoPlainText) { out.append("<code>"); } out.append(linkDescription); if (hasNoPlainText) { if (decl instanceof Function) { out.append("()"); } out.append("</code>"); } if (href != null) { out.append("</a>"); } } static Module resolveModule(Scope scope) { if (scope == null) { return null; } else if (scope instanceof Package) { Package pack = (Package) scope; return pack.getModule(); } else { return resolveModule(scope.getContainer()); } } static Referenceable resolveLink(String linkTarget, Scope linkScope, Unit unit) { if (linkTarget.startsWith("package ")) { Module module = resolveModule(linkScope); return module.getPackage(linkTarget.substring(8).trim()); } if (linkTarget.startsWith("module ")) { Module module = resolveModule(linkScope); Package p = module.getPackage(linkTarget.substring(7).trim()); return p==null ? null : p.getModule(); } String declName; Scope scope = null; int pkgSeparatorIndex = linkTarget.indexOf("::"); if (pkgSeparatorIndex == -1) { declName = linkTarget; scope = linkScope; } else { String pkgName = linkTarget.substring(0, pkgSeparatorIndex); declName = linkTarget.substring(pkgSeparatorIndex+2, linkTarget.length()); Module module = resolveModule(linkScope); if (module != null) { scope = module.getPackage(pkgName); } } if (scope==null || declName == null || "".equals(declName)) { return null; // no point in continuing. Required for non-token auto-complete. } String[] declNames = declName.split("\\."); Declaration decl = scope.getMemberOrParameter(unit, declNames[0], null, false); for (int i=1; i<declNames.length; i++) { if (decl instanceof Scope) { scope = (Scope) decl; decl = scope.getMember(declNames[i], null, false); } else { decl = null; break; } } return decl; } }