Skip to main content

typst_analyzer_analysis/completion/
markup.rs

1/*!
2# Markup
3
4Paragraph break Blank line parbreak
5Strong emphasis *strong* strong
6Emphasis _emphasis_ emph
7Raw text `print(1)` raw
8Link https://typst.app/ link
9Label <intro> label
10Reference @intro ref
11Bullet list - item list
12Numbered list + item enum
13Term list / Term: description terms
14Line break \ linebreak
15Smart quote 'single' or "double" smartquote
16Code expression #rect(width: 1cm) Scripting
17Comment /* block */, // line Below
18TODO: Heading = Heading heading
19TODO: Math $x^2$ Math
20TODO: Symbol shorthand ~, --- Symbols
21TODO: Character escape Tweet at us \#ad Below
22*/
23
24use std::collections::HashMap;
25
26use tower_lsp::lsp_types::{CompletionItem, CompletionItemKind, InsertTextFormat, MarkupContent};
27
28use crate::{get_images, typ_logger};
29
30use super::generic::TypCmpItem;
31
32pub fn items() -> Vec<CompletionItem> {
33    let mut items = Vec::new();
34    items.append(&mut headers());
35    items
36}
37
38pub fn constructors() -> Vec<CompletionItem> {
39    let mut items = Vec::new();
40    // vec of tuples with the constructor name, the label details and insert text
41    let constructor: Vec<(&str, &str, String)> = vec![
42        ("Paragraph break", "parbreak", "parbreak()\n".to_owned()),
43        ("Strong emphasis", "strong", "*${1:Text}*".to_owned()),
44        ("Strong emphasis", "bold", "*${1:Text}*".to_owned()),
45        ("Emphasis", "emph", "_${1:Text}_".to_owned()),
46        ("Raw text", "raw", "`${1:Text}`".to_owned()),
47        ("Link", "link", "https://${1:URL}/".to_owned()),
48        ("Label", "label", "<${1:Text}>".to_owned()),
49        ("Reference", "ref", "@${1:Text}".to_owned()),
50        ("Bullet list", "bullet-list", "- ${1:Text}".to_owned()),
51        ("Numbered list", "numbered-list", "+ ${1:Text}".to_owned()),
52        (
53            "Term list",
54            "terms",
55            "/ ${1:Term}: ${2:Description}".to_owned(),
56        ),
57        ("Line break", "linebreak", "\\".to_owned()),
58        ("Smart quote", "smartquote", "'${1:Text}'".to_owned()),
59        ("Code expression", "Scripting", "#${1:Code}".to_owned()),
60        ("Comment", "line-comment", "// ${1:Text}".to_owned()),
61        ("Comment", "block-comment", "/*\n ${1:Text} \n*/".to_owned()),
62    ];
63    for ctx in constructor {
64        let item = TypCmpItem {
65            label: ctx.1.to_owned(),
66            label_details: "markup",
67            kind: CompletionItemKind::CONSTRUCTOR,
68            documentation: ctx.0,
69            insert_text: ctx.2.to_owned(),
70        };
71        items.push(item);
72    }
73    TypCmpItem::get_cmp(items)
74}
75
76fn get_headers() -> HashMap<String, String> {
77    let mut cmp_item = HashMap::new();
78    let mut item = "=".to_owned();
79
80    // Use a fresh `label` for each iteration
81    for num in 1..7 {
82        let label = format!("h{}", num); // Create the label as "h<num>"
83        cmp_item.insert(label, item.clone());
84        item.push('='); // Add an additional '#' to the item for the next iteration
85    }
86    cmp_item
87}
88
89#[test]
90fn header_test() {
91    let mut expected = HashMap::new();
92    expected.insert("h1".to_owned(), "=".to_owned());
93    expected.insert("h2".to_owned(), "==".to_owned());
94    expected.insert("h3".to_owned(), "===".to_owned());
95    expected.insert("h4".to_owned(), "====".to_owned());
96    expected.insert("h5".to_owned(), "=====".to_owned());
97    expected.insert("h6".to_owned(), "======".to_owned());
98
99    let result = get_headers();
100
101    assert_eq!(
102        result, expected,
103        "The generated HashMap does not match the expected output."
104    );
105}
106
107fn headers() -> Vec<CompletionItem> {
108    let header = get_headers();
109    let mut header_items = Vec::new();
110    for (key, val) in header {
111        let num: usize = key[1..].parse().unwrap_or(0);
112        header_items.push(CompletionItem {
113            label: key.to_owned(),
114            label_details: Some(tower_lsp::lsp_types::CompletionItemLabelDetails {
115                detail: Some("Header".to_owned()),
116                description: None,
117            }),
118            kind: Some(CompletionItemKind::CONSTRUCTOR),
119            documentation: Some(tower_lsp::lsp_types::Documentation::MarkupContent(
120                MarkupContent {
121                    kind: tower_lsp::lsp_types::MarkupKind::Markdown,
122                    value: format!("level {} heading", num),
123                },
124            )),
125            insert_text: Some(format!("{} ", val)),
126            insert_text_format: Some(InsertTextFormat::PLAIN_TEXT),
127            ..Default::default()
128        });
129    }
130    header_items
131}
132
133pub fn typ_image_cmp() -> Result<Vec<CompletionItem>, anyhow::Error> {
134    let mut items = Vec::new();
135    let images = get_images()?;
136        typ_logger!("image: {:#?}", images);
137    for item in images {
138        let image = item.to_string_lossy().to_string();
139        let item = TypCmpItem {
140            label: "image".to_owned(),
141            label_details: "markup",
142            kind: CompletionItemKind::FILE,
143            documentation: "Image",
144            insert_text: format!("#image(\"{}\", width: 100%)", image),
145        };
146        typ_logger!("image: {}", image);
147        items.push(item);
148    }
149    Ok(TypCmpItem::get_cmp(items))
150}