
Entry.pudu
Pudu92 lines3.4 KB
1/** @Domain.Reference.Entry.Aggregate — public declarations and name search */2module PuduLangMcp.Domain.Reference.Entry34import Std.List as List5import Std.Text as Text67/** @Domain.Reference.Entry.Entry — one public declaration */8export type Entry = { moduleName: Str, kind: Str, name: Str, signature: Str, doc: Str }910/** @Domain.Reference.Entry.Found — a ranked declaration match */11export type Found = { entry: Entry, score: Int }121314const EXACT: Int = 1001516const NAME_PREFIX: Int = 601718const QUALIFIED_CONTAINS: Int = 451920const NAME_CONTAINS: Int = 402122const SIGNATURE_CONTAINS: Int = 252324const DOC_CONTAINS: Int = 102526const FUNCTION_BONUS: Int = 5272829export fn modules(entries: &Array[Entry]) -> Array[Str] {30 var seen: Set[Str] = setOf([])31 var names: Array[Str] = []32 for entry in *entries {33 if !seen.contains(entry.moduleName) {34 seen = seen.insert(entry.moduleName)35 names = names.push(entry.moduleName)36 }37 }38 List.sorted(&names)39}404142export fn ofModule(entries: &Array[Entry], moduleName: Str) -> Array[Entry] {43 entries.filter(|entry: Entry| entry.moduleName == moduleName)44}454647export fn render(entry: &Entry) -> Str {48 let head = if entry.signature.isEmpty() { entry.kind + " " + entry.moduleName + "." + entry.name } else { entry.kind + " " + entry.moduleName + "." + entry.name + " :: " + entry.signature }49 if entry.doc.isEmpty() { head } else { head + "\n " + entry.doc.replace("\n", "\n ") }50}515253export fn renderModule(entries: &Array[Entry], moduleName: Str) -> Str {54 let held = ofModule(entries, moduleName)55 if held.isEmpty() { return "" }56 "module " + moduleName + " — " + show(held.length()) + " public declarations\n\n" + Text.join(&held.map(|entry: Entry| render(&entry)), "\n\n")57}585960export fn search(entries: &Array[Entry], query: Str, limit: Int) -> Array[Found] {61 let wanted = query.trim().toLower()62 if wanted.isEmpty() { return [] }63 var ranked: Array[Found] = []64 for entry in *entries {65 let points = score(&entry, wanted)66 if points > 0 { ranked = ranked.push(Found{entry: entry, score: points}) }67 }68 List.take(&List.sortOn(&ranked, |held: Found| 0 - held.score), limit)69}707172fn score(entry: &Entry, wanted: Str) -> Int {73 let name = entry.name.toLower()74 let qualified = entry.moduleName.toLower() + "." + name75 let best = if name == wanted || qualified == wanted {76 EXACT77 } else if name.startsWith(wanted) {78 NAME_PREFIX79 } else if qualified.contains(wanted) {80 QUALIFIED_CONTAINS81 } else if name.contains(wanted) {82 NAME_CONTAINS83 } else if entry.signature.toLower().contains(wanted) {84 SIGNATURE_CONTAINS85 } else if entry.doc.toLower().contains(wanted) {86 DOC_CONTAINS87 } else {88 089 }90 if best > 0 && entry.kind.startsWith("fn") { best + FUNCTION_BONUS } else { best }91}92