logo

class

docCompiler::ApiToHtmlGenerator

sys::Obj
  fandoc::HtmlDocWriter
    docCompiler::HtmlGenerator
      docCompiler::ApiToHtmlGenerator
   1  //
   2  // Copyright (c) 2007, Brian Frank and Andy Frank
   3  // Licensed under the Academic Free License version 3.0
   4  //
   5  // History:
   6  //   5 May 07  Brian Frank  Creation
   7  //
   8  
   9  using compiler
  10  using fandoc
  11  
  12  **
  13  ** ApiToHtmlGenerator generates an HTML file for a Type's API
  14  **
  15  class ApiToHtmlGenerator : HtmlGenerator
  16  {
  17  
  18  //////////////////////////////////////////////////////////////////////////
  19  // Constructor
  20  //////////////////////////////////////////////////////////////////////////
  21  
  22    new make(DocCompiler compiler, Location loc, OutStream out, Type t)
  23      : super(compiler, loc, out)
  24    {
  25      this.t = t
  26      sorter := |Slot a, Slot b -> Int| { return a.name <=> b.name }
  27      filter := |Slot s -> Bool| { return showSlot(t, s) }
  28      this.slots = t.slots.rw.sort(sorter).findAll(filter)
  29    }
  30  
  31  //////////////////////////////////////////////////////////////////////////
  32  // Generator
  33  //////////////////////////////////////////////////////////////////////////
  34  
  35    override Str title()
  36    {
  37      return t.qname
  38    }
  39  
  40    override Void header()
  41    {
  42      out.print("<ul>\n")
  43      out.print("  <li><a href='../index.html'>$docHome</a></li>\n")
  44      out.print("  <li><a href='index.html'>$t.pod.name</a></li>\n")
  45      out.print("  <li>$t.name</li>\n")
  46      out.print("</ul>\n")
  47    }
  48  
  49    override Void content()
  50    {
  51      out.print("<div class='type'>\n")
  52      typeOverview
  53      typeDetail
  54      out.print("</div>\n")
  55      slotsDetail
  56    }
  57  
  58    override Void sidebar()
  59    {
  60      actions
  61      slotsOverview
  62    }
  63  
  64  //////////////////////////////////////////////////////////////////////////
  65  // Methods
  66  //////////////////////////////////////////////////////////////////////////
  67  
  68    **
  69    ** Generate the actions.
  70    **
  71    Void actions()
  72    {
  73      out.print("<h2>More Info</h2>\n")
  74      out.print("<ul>\n")
  75      out.print("  <li><a href='${t.name}_src.html'>View Source</a></li>\n")
  76      out.print("  <li><a href='#' onclick='ShowSlots.toggle(event); return false;'>")
  77      out.print("Show All Slots</a></li>\n")
  78      out.print("</ul>\n")
  79    }
  80  
  81    **
  82    ** Generate the type overview documentation.
  83    **
  84    Void typeOverview()
  85    {
  86      out.print("<div class='overview'>\n")
  87      out.print("<h2>")
  88      if (t.isConst) out.print("const ")
  89      if (t.isMixin) out.print("mixin")
  90      else
  91      {
  92        if (t.isAbstract) out.print("abstract ")
  93        if (t.isEnum)
  94          out.print("enum")
  95        else
  96          out.print("class")
  97      }
  98      out.print("</h2>\n")
  99      out.print("<h1>$t.qname</h1>\n")
 100      if (t.base != null) inheritance
 101      out.print("</div>\n")
 102    }
 103  
 104    **
 105    ** Generate the type detail documentation.
 106    **
 107    Void typeDetail()
 108    {
 109      if (t.doc == null) return
 110      out.print("<div class='detail'>\n")
 111      fandoc(t.doc)
 112      out.print("</div>\n")
 113    }
 114  
 115    **
 116    ** Generate the type inheritance.
 117    **
 118    Void inheritance()
 119    {
 120      chain := Type[t]
 121      base  := t.base
 122      while (base != null)
 123      {
 124        chain.insert(0, base)
 125        base = base.base
 126      }
 127      out.print("<pre>")
 128      chain.each |Type t, Int i|
 129      {
 130        if (i > 0) out.print("\n${Str.spaces(i*2)}")
 131        if (i == chain.size-1) out.print(t.qname)
 132        else out.print("<a href='${compiler.uriMapper.map(t.qname, loc)}'>$t.qname</a>")
 133      }
 134      t.mixins.each |Type t, Int i|
 135      {
 136        //if (i == 0) out.print("\n\nMixin: ")
 137        if (i == 0) out.print(" : ")
 138        else out.print(", ")
 139        out.print("<a href='${compiler.uriMapper.map(t.qname, loc)}'>$t.qname</a>")
 140      }
 141      out.print("</pre>")
 142    }
 143  
 144    **
 145    ** Generate the slot overview documentation.
 146    **
 147    Void slotsOverview(Bool hideByDefault := true)
 148    {
 149      out.print("<div class='slots'>\n")
 150      out.print("<div class='overview'>\n")
 151      out.print("<h2>Slots</h2>\n")
 152      out.print("<ul>\n")
 153      slots.each |Slot slot|
 154      {
 155        if (!showSlot(t, slot)) return
 156        out.print("  <li")
 157        if (!showByDefault(t, slot)) out.print(" class='hidden'")
 158        if (!hideByDefault) out.print(" style='display: block;'")
 159        out.print("><a href='#$slot.name'>$slot.name</a></li>\n")
 160      }
 161      out.print("</ul>\n")
 162      out.print("</div>\n")
 163      out.print("</div>\n")
 164    }
 165  
 166    **
 167    ** Generate the slot detail documentation.
 168    **
 169    Void slotsDetail()
 170    {
 171      out.print("<div class='slots'>\n")
 172      out.print("<div class='detail'>\n")
 173      out.print("<h2>Slots</h2>\n")
 174      out.print("<dl>\n")
 175      slots.each |Slot slot| { slotDetail(slot) }
 176      out.print("</dl>\n")
 177      out.print("</div>\n")
 178      out.print("</div>\n")
 179    }
 180  
 181    **
 182    ** Generate the documentation for the given slot.
 183    **
 184    Void slotDetail(Slot slot)
 185    {
 186      if (!showSlot(t, slot)) return
 187  
 188      oldfile := loc.file
 189      loc.file = slot.qname
 190  
 191      hidden := !showByDefault(t, slot)
 192      cls := (slot.isField) ? "field" : "method"
 193      if (hidden) cls += " hidden"
 194      out.print("<dt id='$slot.name' class='$cls'>$slot.name</dt>\n")
 195      out.print("<dd")
 196      if (hidden) out.print(" class='hidden'")
 197      out.print(">\n")
 198  
 199      // Slot spec
 200      out.print("<p><code>")
 201      if (slot.isField)
 202      {
 203        f := (Field)slot
 204        slotModifiers(f)
 205        typeLink(f.of)
 206        out.print(" $f.name")
 207        setter(f)
 208      }
 209      else
 210      {
 211        m := (Method)slot
 212        if (m.isCtor) out.print("new")
 213        else
 214        {
 215          slotModifiers(m)
 216          typeLink(m.returns)
 217        }
 218        out.print(" $m.name")
 219        out.print("(")
 220        m.params.each |Param p, Int i|
 221        {
 222          if (i > 0) out.print(", ")
 223          typeLink(p.of)
 224          out.print(" $p.name")
 225          if (p.hasDefault) out.print(" := def")
 226        }
 227        out.print(")")
 228      }
 229      out.print("</code></p>\n")
 230  
 231      // inherited
 232      if (isInnherited(t, slot))
 233      {
 234        out.print("<p>Inherited from ")
 235        typeLink(slot.parent)
 236        out.print("</p>\n")
 237      }
 238  
 239      // Slot comment
 240      if (slot.doc != null)
 241        fandoc(slot.doc)
 242  
 243      out.print("</dd>\n")
 244  
 245      loc.file = oldfile
 246    }
 247  
 248    **
 249    ** Write a slot's modifiers.
 250    **
 251    Void slotModifiers(Slot s)
 252    {
 253      if (s.isVirtual)
 254      {
 255        if (s.isAbstract) out.print("abstract ")
 256        else if (s.isOverride) out.print("override ")
 257        else out.print("virtual ")
 258      }
 259  
 260      if (s.isStatic) out.print("static ")
 261      else if (s.isConst) out.print("const ")
 262  
 263      if (s.isProtected) out.print("protected ")
 264      else if (s.isPrivate)   out.print("private ")
 265      else if (s.isInternal)  out.print("internal ")
 266  
 267      if (s.isField)
 268      {
 269        Method z := s->setter
 270        if (z != null && !s.isPrivate && z.isPrivate) out.print("readonly ")
 271      }
 272    }
 273  
 274    **
 275    ** Write a field's setter proctection level if its different
 276    ** from the getter's level.
 277    **
 278    Void setter(Field f)
 279    {
 280      Method s := f->setter
 281      if (s == null) return
 282      if (f.isPublic    && s.isPublic)    return
 283      if (f.isProtected && s.isProtected) return
 284      if (f.isPrivate   && s.isPrivate)   return
 285      if (f.isInternal  && s.isInternal)  return
 286  
 287      // this case handled already in slotModifers() by writing 'readonly'
 288      if (!f.isPrivate && s.isPrivate) return
 289  
 290      // if we made this far, they must be different
 291      out.print(" { ")
 292      slotModifiers(s)
 293      out.print("set }")
 294    }
 295  
 296    **
 297    ** Write a type link out in the form <a href='type.uri'>type.name</a>.
 298    **
 299    Void typeLink(Type t)
 300    {
 301      display := typeToDisplay(t)
 302  
 303      // TODO - not really sure what the right thing to
 304      // do here is - these are the generic types, like
 305      // sys::A, sys::V, etc
 306      if (t.pod.name == "sys" && t.name.size == 1)
 307        t = Obj.type
 308  
 309      out.print("<a href='${compiler.uriMapper.map(t.qname, loc)}'>$display</a>")
 310    }
 311  
 312    **
 313    ** Convert the Type name to a display string by stripping
 314    ** the pod names from the signature.
 315    **
 316    static Str typeToDisplay(Type t)
 317    {
 318      if (!t.isGeneric)
 319      {
 320        p := t.params
 321        if (p["L"] != null) return typeToDisplay(p["V"]) + "[]"
 322        if (p["M"] != null) return typeToDisplay(p["K"]) + ":" + typeToDisplay(p["V"])
 323        if (p["R"] != null)
 324        {
 325          buf := StrBuf.make.addChar('|')
 326  
 327          keys := p.keys.rw.sort |Str a, Str b -> Int| { return a <=> b }
 328          keys.each |Str k, Int i|
 329          {
 330            if (k == "R") return
 331            if (i > 0) buf.add(", ")
 332            buf.add(typeToDisplay(p[k]))
 333          }
 334  
 335          if (p["R"] != Void.type)
 336            buf.add(" -> ").add(typeToDisplay(p["R"]))
 337  
 338          buf.addChar('|')
 339          return buf.toStr
 340        }
 341      }
 342  
 343      // Force sys::A,B,C,etc to be Obj
 344      //if (t.pod.name == "sys" && t.name.size == 1) t = Obj.type
 345      return t.name
 346    }
 347  
 348    **
 349    ** Write out the fandoc for this text - if an exception
 350    ** is thrown, write the original text.
 351    **
 352    Void fandoc(Str text)
 353    {
 354      try
 355      {
 356        doc := FandocParser.make.parse("API for $t", InStream.makeForStr(text))
 357        doc.children.each |DocNode child| { child.write(this) }
 358      }
 359      catch { out.print("<p>$text<p>\n") }
 360    }
 361  
 362  //////////////////////////////////////////////////////////////////////////
 363  // Fields
 364  //////////////////////////////////////////////////////////////////////////
 365  
 366    Type t        // type to documenting
 367    Slot[] slots  // slots to document
 368  
 369  }