Pudu programming language
Menu
Package

@chrismichaelps / pudu-lang-docgen

Documentation publishing for Pudu: articles, API references, navigation, search, and static websites

0.1.0Apache-2.01

InstallClose

Signature.pudu

Pudu81 lines3.4 KB

GitHub ↗
1/** @Docgen.Api.Signature — declarations shown with keywords marked and types linked */2module PuduLangDocgen.Api.Signature34import Std.Map as Map5import Std.Text as Text6import PuduLangDocgen.Api.Lexer as Lexer7import PuduLangDocgen.Api.Model as Model8import PuduLangDocgen as Docgen9import PuduLangDocgen.Paths as Paths1011/// Words marked as keywords inside declarations.12const KEYWORDS: Array[Str] = ["fn", "async", "type", "trait", "impl", "const", "for", "export", "module", "self", "Self"]1314/// The identity a written type name refers to inside a module, or the empty text.15/// `Alias.Name` resolves through the module's imports; a bare name resolves inside the16/// module first and then as a full identity.17export fn resolve(unit: &Model.Unit, written: Str, references: &Map[Str, Docgen.Reference]) -> Str {18  let parts = written.split(".")19  if parts.length() >= 2 {20    let alias = parts[0]21    for (name, imported) in unit.imports {22      if name == alias {23        let uid = imported + "." + parts.slice(1, parts.length()).join(".")24        if Map.containsKey(references, uid) { return uid }25      }26    }27  }28  let local = unit.uid + "." + written29  if Map.containsKey(references, local) { return local }30  if Map.containsKey(references, written) { return written }31  ""32}3334/// Declaration HTML for a page: keywords marked, resolvable names linked, the rest escaped.35/// Line breaks and leading indentation of the declaration are kept.36export fn html(unit: &Model.Unit, signature: Str, references: &Map[Str, Docgen.Reference], page: Str) -> Str {37  signature.split("\n").map(fn(line: Str) -> Str {38      let depth = line.length() - Text.trimStart(line).length()39      line.take(depth) + lineHtml(unit, line, references, page)40    }).join("\n")41}4243/// One declaration line as HTML.44fn lineHtml(unit: &Model.Unit, signature: Str, references: &Map[Str, Docgen.Reference], page: Str) -> Str {45  let tokens = Lexer.tokens(signature)46  var pieces: Array[Str] = []47  var index = 048  var previous = ""49  while index < tokens.length() {50    let token = tokens[index]51    var end = index + 152    var written = token.text53    if token.kind == Lexer.Word {54      while end + 1 < tokens.length() && tokens[end].text == "." && tokens[end + 1].kind == Lexer.Word {55        written = written + "." + tokens[end + 1].text56        end = end + 257      }58    }59    let spaced = !pieces.isEmpty() && ![")", "]", ",", ":", ".", "?"].contains(token.text) && !["(", "[", ".", "&", "*", "!"].contains(previous) && !((token.text == "(" || token.text == "[") && !["->", "=", "|", ",", ":"].contains(previous))60    if spaced { pieces = pieces.push(" ") }61    let uid = if token.kind == Lexer.Word && !KEYWORDS.contains(written) { resolve(unit, written, references) } else { "" }62    if !uid.isEmpty() {63      let reference = Map.get(references, uid)64      let href = match reference {65        case Some(found) => if Paths.remote(found.href) { found.href } else { Paths.between(page, found.href) }66        case None => ""67      }68      pieces = pieces.push("<a class=\"xref\" href=\"" + Paths.escape(href) + "\">" + Paths.escape(written) + "</a>")69    } else if token.kind == Lexer.Word && KEYWORDS.contains(written) {70      pieces = pieces.push("<span class=\"hl-k\">" + written + "</span>")71    } else if token.kind == Lexer.Literal {72      pieces = pieces.push("<span class=\"hl-s\">" + Paths.escape(written) + "</span>")73    } else {74      pieces = pieces.push(Paths.escape(written))75    }76    previous = tokens[end - 1].text77    index = end78  }79  pieces.join("")80}81