logicaffeine_lsp/
stdlib_docs.rs1use std::collections::HashMap;
9use std::sync::OnceLock;
10
11use logicaffeine_language::teach::extract_literate_docs;
12
13pub struct StdlibDoc {
15 pub signature: String,
17 pub doc: Option<String>,
19 pub is_type: bool,
21}
22
23fn registry() -> &'static HashMap<String, StdlibDoc> {
24 static DOCS: OnceLock<HashMap<String, StdlibDoc>> = OnceLock::new();
25 DOCS.get_or_init(|| {
26 let mut map = HashMap::new();
27 for src in logicaffeine_compile::loader::prelude_module_sources() {
28 for lit in extract_literate_docs(src) {
29 let is_type = !lit.signature.contains("## To");
30 map.entry(lit.name).or_insert(StdlibDoc {
31 signature: lit.signature,
32 doc: lit.doc,
33 is_type,
34 });
35 }
36 }
37 map
38 })
39}
40
41pub fn stdlib_doc(name: &str) -> Option<&'static StdlibDoc> {
43 registry().get(name)
44}
45
46pub fn all() -> Vec<(&'static str, &'static StdlibDoc)> {
48 let mut entries: Vec<(&'static str, &'static StdlibDoc)> =
49 registry().iter().map(|(name, doc)| (name.as_str(), doc)).collect();
50 entries.sort_by_key(|(name, _)| *name);
51 entries
52}
53
54pub fn hover_md(name: &str, entry: &StdlibDoc) -> String {
56 let mut md = format!(
57 "**{name}** — standard library\n\n```\n{}\n```",
58 entry.signature
59 );
60 if let Some(doc) = &entry.doc {
61 md.push_str("\n\n");
62 md.push_str(doc);
63 }
64 md
65}
66
67#[cfg(test)]
68mod tests {
69 use super::*;
70
71 #[test]
72 fn the_registry_covers_the_prelude_vocabulary() {
73 for name in logicaffeine_compile::loader::prelude_vocabulary() {
74 assert!(
75 stdlib_doc(&name).is_some(),
76 "{name}: in prelude_vocabulary but missing from the stdlib docs registry"
77 );
78 }
79 }
80
81 #[test]
82 fn md5_teaches_from_its_literate_note() {
83 let entry = stdlib_doc("md5").expect("md5 is stdlib");
84 assert!(entry.signature.contains("md5"), "{}", entry.signature);
85 assert!(
86 entry.doc.as_deref().is_some_and(|d| d.contains("MD5")),
87 "md5's Note must teach: {:?}",
88 entry.doc
89 );
90 assert!(!entry.is_type);
91 }
92
93 #[test]
94 fn definition_body_types_are_typed_entries() {
95 let entry = stdlib_doc("Message").expect("net.md defines Message");
96 assert!(entry.is_type);
97 assert!(entry.doc.is_some(), "the Definition block's Note documents Message");
98 }
99
100 #[test]
101 fn hover_md_carries_signature_and_prose() {
102 let entry = stdlib_doc("read").expect("file.lg defines read");
103 let md = hover_md("read", entry);
104 assert!(md.contains("standard library"), "{md}");
105 assert!(md.contains("## To native read"), "{md}");
106 assert!(md.contains("Reads a whole file"), "{md}");
107 }
108}