@Override
 public String getHyperlinkText() {
   Backends supportedBackends = supportedBackends();
   return "Ceylon Declaration"
       + (supportedBackends.none()
           ? ""
           : " \u2014 "
               + (supportedBackends.header()
                   ? "native header"
                   : supportedBackends + " backend implementation"));
 }
 @Override
 public IHyperlink[] detectHyperlinks(
     ITextViewer textViewer, IRegion region, boolean canShowMultipleHyperlinks) {
   if (controller == null || controller.getLastCompilationUnit() == null) {
     return null;
   } else {
     Node node =
         findNode(
             controller.getLastCompilationUnit(),
             controller.getTokens(),
             region.getOffset(),
             region.getOffset() + region.getLength());
     if (node == null) {
       return null;
     } else {
       Node id = getIdentifyingNode(node);
       if (id == null) {
         return null;
       } else {
         Referenceable referenceable = getReferencedModel(node);
         Backends supportedBackends = supportedBackends();
         if (referenceable instanceof Declaration) {
           Declaration dec = (Declaration) referenceable;
           if (dec.isNative()) {
             if (supportedBackends.none()) {
               return null;
             } else {
               referenceable = resolveNative(referenceable, dec, supportedBackends);
             }
           } else {
             if (!supportedBackends.none()) {
               return null;
             }
           }
         } else { // Module or package descriptors
           if (!supportedBackends.none()) {
             return null;
           }
         }
         Node r = getReferencedNode(referenceable);
         if (r == null) {
           return null;
         } else {
           return new IHyperlink[] {new CeylonNodeLink(r, id)};
         }
       }
     }
   }
 }
  private void writeDependencyRow(ModuleImport moduleImport) throws IOException {
    StringBuilder tooltip = new StringBuilder();
    if (moduleImport.isExport()) {
      tooltip.append("shared ");
    }
    if (moduleImport.isOptional()) {
      tooltip.append("optional ");
    }
    tooltip.append("import of module ");
    tooltip.append(moduleImport.getModule().getNameAsString());
    tooltip.append(" ");
    tooltip.append(moduleImport.getModule().getVersion());

    open("tr");

    open("td class='shrink'");
    open("span title='" + tooltip + "'");
    writeIcon(moduleImport);
    close("span");
    open("code class='decl-label'");
    linkRenderer().to(moduleImport.getModule()).write();
    Backends backends = moduleImport.getNativeBackends();
    if (!backends.none()) {
      write(" (");
      write(backends.names());
      write(")");
    }
    close("code");
    close("td");

    open("td class='shrink'");
    open("code");
    write(moduleImport.getModule().getVersion());
    close("code");
    close("td");

    open("td");
    open("div class='description import-description'");
    write(getDoc(moduleImport, linkRenderer()));
    close("div");
    close("td");

    close("tr");
  }
  private Referenceable resolveNative(
      Referenceable referenceable, Declaration dec, Backends backends) {
    Unit unit = dec.getUnit();
    Scope containerToSearchHeaderIn = null;
    if (unit instanceof CeylonBinaryUnit) {
      CeylonBinaryUnit binaryUnit = (CeylonBinaryUnit) unit;
      ExternalPhasedUnit phasedUnit = binaryUnit.getPhasedUnit();
      if (phasedUnit != null) {
        ExternalSourceFile sourceFile = phasedUnit.getUnit();
        if (sourceFile != null) {
          String sourceRelativePath =
              toJavaString(
                  binaryUnit
                      .getCeylonModule()
                      .toSourceUnitRelativePath(toCeylonString(unit.getRelativePath())));
          if (sourceRelativePath != null && sourceRelativePath.endsWith(".ceylon")) {
            for (Declaration sourceDecl : sourceFile.getDeclarations()) {
              if (sourceDecl.equals(dec)) {
                containerToSearchHeaderIn = sourceDecl.getContainer();
                break;
              }
            }
          } else {
            for (Declaration sourceDecl : sourceFile.getDeclarations()) {
              if (sourceDecl.getQualifiedNameString().equals(dec.getQualifiedNameString())) {
                containerToSearchHeaderIn = sourceDecl.getContainer();
                break;
              }
            }
          }
        }
      }
    } else {
      containerToSearchHeaderIn = dec.getContainer();
    }

    if (containerToSearchHeaderIn != null) {
      Declaration headerDeclaration = getNativeHeader(containerToSearchHeaderIn, dec.getName());
      if (headerDeclaration == null || !headerDeclaration.isNative()) return null;
      if (backends.header()) {
        referenceable = headerDeclaration;
      } else {
        if (headerDeclaration != null) {
          referenceable = getNativeDeclaration(headerDeclaration, supportedBackends());
        }
      }
    }
    return referenceable;
  }