logicaffeine_lsp/
teach_md.rs1use tower_lsp::lsp_types::{Documentation, MarkupContent, MarkupKind};
10
11use logicaffeine_language::teach::ConstructDoc;
12
13pub fn guide_url(anchor: &str) -> String {
15 format!(
16 "https://github.com/Brahmastra-Labs/logicaffeine/blob/main/LOGOS_QUICKGUIDE.md#{anchor}"
17 )
18}
19
20pub fn keyword_hover_md(doc: &ConstructDoc) -> String {
23 let mut md = format!(
24 "**{}**\n\n{}\n\n```\n{}\n```\n\n{}",
25 doc.name, doc.what, doc.example, doc.question_or_tip
26 );
27 push_guide_link(&mut md, doc);
28 md
29}
30
31pub fn block_hover_md(doc: &ConstructDoc) -> String {
33 let mut md = format!(
34 "**Block Header** — {}\n\n```\n{}\n```\n\n{}",
35 doc.what, doc.example, doc.question_or_tip
36 );
37 push_guide_link(&mut md, doc);
38 md
39}
40
41pub fn completion_docs(doc: &ConstructDoc) -> Documentation {
44 Documentation::MarkupContent(MarkupContent {
45 kind: MarkupKind::Markdown,
46 value: keyword_hover_md(doc),
47 })
48}
49
50fn push_guide_link(md: &mut String, doc: &ConstructDoc) {
51 if let Some(anchor) = doc.guide_anchor {
52 md.push_str(&format!("\n\n[Quick Guide]({})", guide_url(anchor)));
53 }
54}
55
56#[cfg(test)]
57mod tests {
58 use super::*;
59 use logicaffeine_language::teach::{doc_for, doc_for_block};
60 use logicaffeine_language::token::{BlockType, TokenType};
61
62 #[test]
63 fn keyword_rendering_carries_every_lesson_part() {
64 let lesson = doc_for(&TokenType::Give).unwrap();
65 let md = keyword_hover_md(lesson);
66 assert!(md.contains("**Give**"), "{md}");
67 assert!(md.contains(lesson.what), "{md}");
68 assert!(md.contains(lesson.example), "{md}");
69 assert!(md.contains(lesson.question_or_tip), "{md}");
70 assert!(md.contains("LOGOS_QUICKGUIDE.md#13-output"), "{md}");
71 }
72
73 #[test]
74 fn block_rendering_keeps_the_banner() {
75 let lesson = doc_for_block(&BlockType::Main);
76 let md = block_hover_md(lesson);
77 assert!(md.starts_with("**Block Header** — "), "{md}");
78 assert!(md.contains(lesson.question_or_tip), "{md}");
79 }
80
81 #[test]
82 fn lessons_without_anchors_render_without_links() {
83 let lesson = doc_for(&TokenType::Escape).unwrap();
84 let md = keyword_hover_md(lesson);
85 assert!(!md.contains("Quick Guide"), "Escape has no guide section: {md}");
86 }
87
88 #[test]
89 fn completion_docs_match_hover_exactly() {
90 let lesson = doc_for(&TokenType::Let).unwrap();
91 let Documentation::MarkupContent(content) = completion_docs(lesson) else {
92 panic!("expected markup docs");
93 };
94 assert_eq!(content.kind, MarkupKind::Markdown);
95 assert_eq!(content.value, keyword_hover_md(lesson));
96 }
97}